Spring Boot 3 微服务完全搭建指南

作者:Administrator 发布时间: 2026-03-12 阅读量:5 评论数:0

Spring Boot 3 微服务完全搭建指南

从零开始搭建企业级微服务架构,包含完整代码和详细注释


目录

  1. 项目概述

  2. 环境准备

  3. 基础架构搭建

  4. 核心组件集成

  5. 工具类和配置类

  6. 部署和运维

  7. 完整项目结构


1. 项目概述

1.1 技术栈

组件

版本

用途

Spring Boot

3.2.x

基础框架

Spring Cloud

2023.0.x

微服务套件

Nacos

2.2.x

注册中心/配置中心

Gateway

4.x

网关服务

MyBatis-Flex

1.8.x

ORM框架

Redis

7.x

缓存/分布式锁

RabbitMQ

3.12.x

消息队列

MinIO

2024.x

对象存储

MySQL

8.0

关系数据库

Docker

24.x

容器化部署

1.2 架构图

┌─────────────────────────────────────────────────────────────┐
│                        客户端层                              │
│                   (Web/App/小程序)                           │
└────────────────────┬────────────────────────────────────────┘
                     │
┌────────────────────▼────────────────────────────────────────┐
│                      网关层 (Gateway)                        │
│              • 路由转发    • 鉴权认证    • 限流熔断           │
└────────────────────┬────────────────────────────────────────┘
                     │
┌────────────────────▼────────────────────────────────────────┐
│                     服务注册中心 (Nacos)                     │
│              • 服务注册    • 配置管理    • 服务发现           │
└────────────────────┬────────────────────────────────────────┘
                     │
        ┌────────────┼────────────┐
        │            │            │
┌───────▼──────┐ ┌───▼────┐ ┌────▼──────┐
│  业务服务A    │ │业务服务B│ │  业务服务C │
│ • MyBatis-Flex│ │  ...   │ │   ...     │
│ • Redis      │ │        │ │           │
│ • RabbitMQ   │ │        │ │           │
│ • MinIO      │ │        │ │           │
└──────────────┘ └────────┘ └───────────┘

2. 环境准备

2.1 安装Docker和Docker Compose

# 安装Docker(以CentOS/Ubuntu为例)
# CentOS
curl -fsSL https://get.docker.com | sh
​
# Ubuntu
curl -fsSL https://get.docker.com | sudo sh
​
# 启动Docker
sudo systemctl start docker
sudo systemctl enable docker
​
# 安装Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

2.2 创建基础设施Docker Compose文件

创建 infrastructure/docker-compose.yml

# infrastructure/docker-compose.yml
# 基础设施服务编排文件
# 包含:MySQL、Redis、RabbitMQ、MinIO、Nacos
​
version: '3.8'
​
services:
  # ============================================
  # MySQL 8.0 - 关系型数据库
  # ============================================
  mysql:
    image: mysql:8.0
    container_name: mysql
    restart: always
    environment:
      # root用户密码
      MYSQL_ROOT_PASSWORD: root123456
      # 创建默认数据库
      MYSQL_DATABASE: micro_service
      # 时区设置
      TZ: Asia/Shanghai
    ports:
      - "3306:3306"
    volumes:
      # 数据持久化
      - ./mysql/data:/var/lib/mysql
      # 自定义配置文件
      - ./mysql/conf:/etc/mysql/conf.d
      # 初始化SQL脚本
      - ./mysql/init:/docker-entrypoint-initdb.d
    command: 
      - --default-authentication-plugin=mysql_native_password
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
    networks:
      - micro-service-network
​
  # ============================================
  # Redis 7.x - 缓存和分布式锁
  # ============================================
  redis:
    image: redis:7-alpine
    container_name: redis
    restart: always
    ports:
      - "6379:6379"
    volumes:
      # 数据持久化
      - ./redis/data:/data
      # 配置文件
      - ./redis/redis.conf:/usr/local/etc/redis/redis.conf
    command: redis-server /usr/local/etc/redis/redis.conf
    networks:
      - micro-service-network
​
  # ============================================
  # RabbitMQ 3.12 - 消息队列
  # ============================================
  rabbitmq:
    image: rabbitmq:3.12-management-alpine
    container_name: rabbitmq
    restart: always
    environment:
      # 默认用户
      RABBITMQ_DEFAULT_USER: admin
      # 默认密码
      RABBITMQ_DEFAULT_PASS: admin123456
      TZ: Asia/Shanghai
    ports:
      # AMQP协议端口
      - "5672:5672"
      # 管理界面端口
      - "15672:15672"
    volumes:
      - ./rabbitmq/data:/var/lib/rabbitmq
    networks:
      - micro-service-network
​
  # ============================================
  # MinIO - 对象存储
  # ============================================
  minio:
    image: minio/minio:latest
    container_name: minio
    restart: always
    environment:
      # 根用户
      MINIO_ROOT_USER: minioadmin
      # 根密码
      MINIO_ROOT_PASSWORD: minioadmin123
      TZ: Asia/Shanghai
    ports:
      # API端口
      - "9000:9000"
      # 控制台端口
      - "9001:9001"
    volumes:
      - ./minio/data:/data
    # 启动命令:server /data 指定数据目录,--console-address 指定控制台地址
    command: server /data --console-address ":9001"
    networks:
      - micro-service-network
​
  # ============================================
  # Nacos 2.2 - 注册中心和配置中心
  # ============================================
  nacos:
    image: nacos/nacos-server:v2.2.3
    container_name: nacos
    restart: always
    environment:
      #  standalone: 单机模式
      MODE: standalone
      # 使用MySQL作为数据源
      SPRING_DATASOURCE_PLATFORM: mysql
      # MySQL服务地址
      MYSQL_SERVICE_HOST: mysql
      # MySQL端口
      MYSQL_SERVICE_PORT: 3306
      # MySQL数据库名
      MYSQL_SERVICE_DB_NAME: nacos
      # MySQL用户名
      MYSQL_SERVICE_USER: root
      # MySQL密码
      MYSQL_SERVICE_PASSWORD: root123456
      TZ: Asia/Shanghai
    ports:
      # 主端口
      - "8848:8848"
      # gRPC端口(Nacos 2.x新增)
      - "9848:9848"
      # gRPC端口
      - "9849:9849"
    volumes:
      - ./nacos/logs:/home/nacos/logs
      - ./nacos/data:/home/nacos/data
    depends_on:
      - mysql
    networks:
      - micro-service-network
​
# 定义网络,所有服务在同一网络下可以互相通信
networks:
  micro-service-network:
    driver: bridge

2.3 创建配置文件

创建 infrastructure/redis/redis.conf

# Redis配置文件
# 开启持久化
appendonly yes
# 持久化文件目录
appendfsync everysec
# 设置密码(生产环境必须)
requirepass redis123456
# 最大内存限制
maxmemory 512mb
# 内存淘汰策略
maxmemory-policy allkeys-lru
# 绑定所有IP
bind 0.0.0.0
# 关闭保护模式(Docker环境需要)
protected-mode no

创建 infrastructure/mysql/init/01-schema.sql

-- MySQL初始化脚本
-- 创建Nacos所需数据库
​
-- 创建nacos数据库
CREATE DATABASE IF NOT EXISTS nacos CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
​
-- 创建业务数据库
CREATE DATABASE IF NOT EXISTS micro_service CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
​
-- 使用业务数据库
USE micro_service;
​
-- 创建示例用户表
CREATE TABLE IF NOT EXISTS sys_user (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
    username VARCHAR(50) NOT NULL COMMENT '用户名',
    password VARCHAR(100) NOT NULL COMMENT '密码',
    email VARCHAR(100) COMMENT '邮箱',
    phone VARCHAR(20) COMMENT '手机号',
    avatar VARCHAR(500) COMMENT '头像URL',
    status TINYINT DEFAULT 1 COMMENT '状态:0-禁用,1-启用',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    UNIQUE KEY uk_username (username),
    INDEX idx_email (email),
    INDEX idx_phone (phone)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户表';
​
-- 插入测试数据
INSERT INTO sys_user (username, password, email, phone, status) VALUES
('admin', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5E', 'admin@example.com', '13800138000', 1),
('test', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5E', 'test@example.com', '13800138001', 1);

2.4 启动基础设施

# 创建目录结构
mkdir -p infrastructure/{mysql/{data,conf,init},redis/data,rabbitmq/data,minio/data,nacos/{logs,data}}
​
# 启动所有服务
cd infrastructure
docker-compose up -d
​
# 查看服务状态
docker-compose ps
​
# 查看日志
docker-compose logs -f nacos

2.5 验证服务

服务

地址

说明

Nacos

http://localhost:8848/nacos

账号/密码:nacos/nacos

RabbitMQ

http://localhost:15672

账号/密码:admin/admin123456

MinIO

http://localhost:9001

账号/密码:minioadmin/minioadmin123

MySQL

localhost:3306

root/root123456

Redis

localhost:6379

密码:redis123456


3. 基础架构搭建

3.1 父工程 POM 配置

创建 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
​
    <!-- 
        父工程配置
        统一管理所有子模块的依赖版本
    -->
    <groupId>com.example</groupId>
    <artifactId>micro-service-parent</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>
    <name>Micro Service Parent</name>
    <description>微服务父工程</description>
​
    <!-- 子模块列表 -->
    <modules>
        <module>micro-gateway</module>
        <module>micro-common</module>
        <module>micro-service-user</module>
    </modules>
​
    <properties>
        <!-- Java版本 -->
        <java.version>17</java.version>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
​
        <!-- Spring Boot版本 -->
        <spring-boot.version>3.2.0</spring-boot.version>
        <!-- Spring Cloud版本 -->
        <spring-cloud.version>2023.0.0</spring-cloud.version>
        <!-- Spring Cloud Alibaba版本 -->
        <spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
​
        <!-- 组件版本 -->
        <mybatis-flex.version>1.8.0</mybatis-flex.version>
        <druid.version>1.2.20</druid.version>
        <minio.version>8.5.7</minio.version>
        <hutool.version>5.8.23</hutool.version>
        <lombok.version>1.18.30</lombok.version>
        <mapstruct.version>1.5.5.Final</mapstruct.version>
    </properties>
​
    <dependencyManagement>
        <dependencies>
            <!-- Spring Boot依赖管理 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
​
            <!-- Spring Cloud依赖管理 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
​
            <!-- Spring Cloud Alibaba依赖管理 -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
​
            <!-- MyBatis-Flex -->
            <dependency>
                <groupId>com.mybatis-flex</groupId>
                <artifactId>mybatis-flex-spring-boot3-starter</artifactId>
                <version>${mybatis-flex.version}</version>
            </dependency>
​
            <!-- Druid连接池 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-3-starter</artifactId>
                <version>${druid.version}</version>
            </dependency>
​
            <!-- MinIO客户端 -->
            <dependency>
                <groupId>io.minio</groupId>
                <artifactId>minio</artifactId>
                <version>${minio.version}</version>
            </dependency>
​
            <!-- Hutool工具类 -->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>${hutool.version}</version>
            </dependency>
​
            <!-- MapStruct对象映射 -->
            <dependency>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct</artifactId>
                <version>${mapstruct.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
​
    <build>
        <plugins>
            <!-- Maven编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                    <annotationProcessorPaths>
                        <!-- Lombok处理器 -->
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                        <!-- MapStruct处理器 -->
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${mapstruct.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

3.2 网关服务 (micro-gateway)

创建 micro-gateway/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
​
    <parent>
        <groupId>com.example</groupId>
        <artifactId>micro-service-parent</artifactId>
        <version>1.0.0</version>
    </parent>
​
    <artifactId>micro-gateway</artifactId>
    <name>Micro Gateway</name>
    <description>网关服务</description>
​
    <dependencies>
        <!-- Spring Cloud Gateway -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
​
        <!-- Nacos服务发现 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
​
        <!-- Nacos配置中心 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
​
        <!-- 负载均衡 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
​
        <!-- Spring Boot Actuator监控 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
​
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
            </plugin>
        </plugins>
    </build>
</project>

创建 micro-gateway/src/main/java/com/example/gateway/GatewayApplication.java

package com.example.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * 网关服务启动类
 * 
 * @SpringBootApplication: 标记为Spring Boot应用
 * @EnableDiscoveryClient: 启用服务发现,注册到Nacos
 */
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
        System.out.println("=== 网关服务启动成功 ===");
    }
}

创建 micro-gateway/src/main/resources/application.yml

# 网关服务主配置文件
# 注意:实际配置从Nacos配置中心读取

server:
  port: 8080  # 网关服务端口

spring:
  application:
    name: micro-gateway  # 服务名称,注册到Nacos时使用

  profiles:
    active: dev  # 激活的环境配置

  cloud:
    nacos:
      # 配置中心
      config:
        server-addr: 127.0.0.1:8848  # Nacos地址
        namespace: dev               # 命名空间
        group: DEFAULT_GROUP         # 配置分组
        file-extension: yaml         # 配置文件格式
        # 共享配置
        shared-configs:
          - data-id: common.yaml
            group: DEFAULT_GROUP
            refresh: true
      # 服务发现
      discovery:
        server-addr: 127.0.0.1:8848
        namespace: dev
        group: DEFAULT_GROUP

创建 micro-gateway/src/main/resources/bootstrap.yml

# 引导配置文件
# 在应用启动时最先加载,用于配置Nacos连接

spring:
  application:
    name: micro-gateway
  profiles:
    active: dev
  cloud:
    nacos:
      username: nacos
      password: nacos

在Nacos配置中心创建 micro-gateway-dev.yaml

# Nacos配置中心的网关路由配置

server:
  port: 8080

spring:
  cloud:
    gateway:
      # 全局默认过滤器
      default-filters:
        # 添加响应头
        - AddResponseHeader=X-Response-Default-Foo, Default-Bar
        # 重试过滤器
        - name: Retry
          args:
            retries: 3
            statuses: BAD_GATEWAY
            methods: GET,POST
      
      # 路由配置
      routes:
        # 用户服务路由
        - id: user-service-route
          # lb:// 表示使用负载均衡,从Nacos获取服务实例
          uri: lb://micro-service-user
          predicates:
            # 路径匹配:/api/user/** 转发到用户服务
            - Path=/api/user/**
          filters:
            # 去掉路径前缀:/api/user/login -> /login
            - StripPrefix=2
            # 添加前缀:最终变成 /user/login
            - PrefixPath=/user
            # 请求限流
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10    # 每秒补充10个令牌
                redis-rate-limiter.burstCapacity: 20    # 令牌桶容量20
                key-resolver: "#{@ipKeyResolver}"       # 使用IP限流

        # 文件服务路由
        - id: file-service-route
          uri: lb://micro-service-file
          predicates:
            - Path=/api/file/**
          filters:
            - StripPrefix=2
            - PrefixPath=/file

      # 全局跨域配置
      globalcors:
        cors-configurations:
          '[/**]':  # 匹配所有路径
            allowedOrigins: "*"           # 允许所有来源(生产环境应限制)
            allowedMethods: "*"           # 允许所有方法
            allowedHeaders: "*"           # 允许所有请求头
            allowCredentials: true        # 允许携带cookie
            maxAge: 3600                  # 预检请求缓存时间

# 日志配置
logging:
  level:
    org.springframework.cloud.gateway: DEBUG
    reactor.netty: INFO

3.3 公共模块 (micro-common)

创建 micro-common/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.example</groupId>
        <artifactId>micro-service-parent</artifactId>
        <version>1.0.0</version>
    </parent>

    <artifactId>micro-common</artifactId>
    <name>Micro Common</name>
    <description>公共模块:工具类、配置类、常量等</description>
    <packaging>jar</packaging>

    <dependencies>
        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring Data Redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!-- 连接池 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

        <!-- Spring AMQP -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

        <!-- MyBatis-Flex -->
        <dependency>
            <groupId>com.mybatis-flex</groupId>
            <artifactId>mybatis-flex-spring-boot3-starter</artifactId>
        </dependency>

        <!-- Druid连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-3-starter</artifactId>
        </dependency>

        <!-- MySQL驱动 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
        </dependency>

        <!-- MinIO -->
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
        </dependency>

        <!-- Hutool -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>

        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- MapStruct -->
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
        </dependency>

        <!-- Validation -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <!-- JSON处理 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
    </dependencies>
</project>

4. 核心组件集成

4.1 MyBatis-Flex 集成

创建 micro-common/src/main/java/com/example/common/config/MybatisFlexConfig.java

package com.example.common.config;

import com.mybatisflex.core.audit.AuditManager;
import com.mybatisflex.core.audit.ConsoleMessageCollector;
import com.mybatisflex.core.logicdelete.LogicDeleteManager;
import com.mybatisflex.core.logicdelete.impl.BooleanLogicDeleteProcessor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

/**
 * MyBatis-Flex 配置类
 * 
 * MyBatis-Flex 是一个优雅的MyBatis增强框架,特点:
 * 1. 零配置,开箱即用
 * 2. 支持ActiveRecord模式
 * 3. 强大的代码生成器
 * 4. 完善的逻辑删除支持
 */
@Slf4j
@Configuration
public class MybatisFlexConfig {

    /**
     * 初始化配置
     * 
     * @PostConstruct: 在依赖注入完成后执行
     */
    @PostConstruct
    public void init() {
        // 开启SQL审计,打印执行的SQL(开发环境建议开启,生产环境关闭)
        AuditManager.setAuditEnable(true);
        // 设置SQL收集器,输出到控制台
        AuditManager.setMessageCollector(new ConsoleMessageCollector());
        
        // 设置逻辑删除处理器
        // BooleanLogicDeleteProcessor: 使用布尔字段,删除时设置为true
        LogicDeleteManager.setProcessor(new BooleanLogicDeleteProcessor());
        
        log.info("=== MyBatis-Flex 配置初始化完成 ===");
    }
}

创建 micro-common/src/main/java/com/example/common/entity/BaseEntity.java

package com.example.common.entity;

import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.LogicDelete;
import com.mybatisflex.core.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * 基础实体类
 * 
 * 所有业务实体都应该继承此类
 * 提供了通用的字段:ID、创建时间、更新时间、逻辑删除标志
 * 
 * @param <T> 实体类型,用于ActiveRecord模式
 */
@Data
@EqualsAndHashCode(callSuper = false)
public abstract class BaseEntity<T extends Model<T>> extends Model<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 主键ID
     * @Id: 标记为主键
     * @KeyType: 主键生成策略,Auto表示数据库自增
     */
    @Id(keyType = KeyType.Auto)
    private Long id;

    /**
     * 创建时间
     * @Column: 映射数据库字段
     * onInsertValue: 插入时自动填充当前时间
     */
    @Column(onInsertValue = "now()")
    private LocalDateTime createTime;

    /**
     * 更新时间
     * onInsertValue: 插入时填充
     * onUpdateValue: 更新时自动填充当前时间
     */
    @Column(onInsertValue = "now()", onUpdateValue = "now()")
    private LocalDateTime updateTime;

    /**
     * 逻辑删除标志
     * @LogicDelete: 标记为逻辑删除字段
     * true: 已删除, false: 未删除
     */
    @LogicDelete
    private Boolean deleted;
}

4.2 Redis 集成

创建 micro-common/src/main/java/com/example/common/config/RedisConfig.java

package com.example.common.config;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Redis 配置类
 * 
 * 配置RedisTemplate,用于操作Redis
 * 配置了Key和Value的序列化方式
 */
@Configuration
@EnableCaching  // 开启Spring Cache注解支持
public class RedisConfig {

    /**
     * 配置RedisTemplate
     * 
     * RedisTemplate是Spring提供的Redis操作模板
     * 需要配置序列化器,否则存储的数据会乱码
     * 
     * @param connectionFactory Redis连接工厂
     * @return 配置好的RedisTemplate
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        // Key序列化器:使用StringRedisSerializer
        // 将Key序列化为字符串,便于阅读
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringSerializer);
        template.setHashKeySerializer(stringSerializer);

        // Value序列化器:使用GenericJackson2JsonRedisSerializer
        // 将Value序列化为JSON格式,支持复杂对象
        ObjectMapper mapper = new ObjectMapper();
        // 启用类型信息,反序列化时能正确还原对象类型
        mapper.activateDefaultTyping(
            LaissezFaireSubTypeValidator.instance,
            ObjectMapper.DefaultTyping.NON_FINAL,
            JsonTypeInfo.As.PROPERTY
        );
        GenericJackson2JsonRedisSerializer jsonSerializer = 
            new GenericJackson2JsonRedisSerializer(mapper);
        
        template.setValueSerializer(jsonSerializer);
        template.setHashValueSerializer(jsonSerializer);

        // 初始化模板
        template.afterPropertiesSet();
        return template;
    }
}

创建 micro-common/src/main/java/com/example/common/util/RedisUtil.java

package com.example.common.util;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具类
 * 
 * 封装了常用的Redis操作,简化使用
 * 包含:String、Hash、List、Set、ZSet、分布式锁等操作
 */
@Slf4j
@Component
@RequiredArgsConstructor  // Lombok生成包含final字段的构造器
public class RedisUtil {

    private final RedisTemplate<String, Object> redisTemplate;

    // ==================== String操作 ====================

    /**
     * 设置String值
     * 
     * @param key 键
     * @param value 值
     */
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 设置String值,带过期时间
     * 
     * @param key 键
     * @param value 值
     * @param timeout 过期时间
     * @param unit 时间单位
     */
    public void set(String key, Object value, long timeout, TimeUnit unit) {
        redisTemplate.opsForValue().set(key, value, timeout, unit);
    }

    /**
     * 获取String值
     * 
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 删除Key
     * 
     * @param key 键
     * @return 是否成功
     */
    public Boolean delete(String key) {
        return redisTemplate.delete(key);
    }

    /**
     * 判断Key是否存在
     * 
     * @param key 键
     * @return 是否存在
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 设置过期时间
     * 
     * @param key 键
     * @param timeout 过期时间
     * @param unit 时间单位
     * @return 是否成功
     */
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }

    // ==================== 分布式锁 ====================

    /**
     * 获取分布式锁(简单版)
     * 
     * 使用SETNX命令实现
     * 可能存在死锁问题,生产环境建议使用Redisson
     * 
     * @param lockKey 锁的Key
     * @param value 锁的值(通常使用UUID)
     * @param expireTime 过期时间(秒)
     * @return 是否获取成功
     */
    public Boolean tryLock(String lockKey, String value, long expireTime) {
        Boolean success = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, value, expireTime, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);
    }

    /**
     * 释放分布式锁(Lua脚本保证原子性)
     * 
     * @param lockKey 锁的Key
     * @param value 锁的值(必须与获取时一致)
     * @return 是否释放成功
     */
    public Boolean unlock(String lockKey, String value) {
        // Lua脚本:判断值是否匹配,匹配则删除
        String script = 
            "if redis.call('get', KEYS[1]) == ARGV[1] then " +
            "    return redis.call('del', KEYS[1]) " +
            "else " +
            "    return 0 " +
            "end";
        
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(Long.class);
        
        Long result = redisTemplate.execute(redisScript, 
            Collections.singletonList(lockKey), value);
        
        return result != null && result == 1;
    }
}

4.3 RabbitMQ 集成

创建 micro-common/src/main/java/com/example/common/config/RabbitMQConfig.java

package com.example.common.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * RabbitMQ 配置类
 * 
 * 配置交换机、队列、绑定关系
 * 配置消息转换器
 */
@Slf4j
@Configuration
public class RabbitMQConfig {

    // ==================== 交换机定义 ====================

    /**
     * 直连交换机(Direct Exchange)
     * 
     * 特点:精确匹配Routing Key
     * 适用场景:点对点消息发送
     */
    @Bean
    public DirectExchange directExchange() {
        return new DirectExchange("direct.exchange");
    }

    /**
     * 主题交换机(Topic Exchange)
     * 
     * 特点:支持通配符匹配Routing Key
     * 适用场景:发布/订阅模式,如日志分类
     */
    @Bean
    public TopicExchange topicExchange() {
        return new TopicExchange("topic.exchange");
    }

    /**
     * 扇形交换机(Fanout Exchange)
     * 
     * 特点:广播到所有绑定的队列,忽略Routing Key
     * 适用场景:广播消息
     */
    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange("fanout.exchange");
    }

    // ==================== 队列定义 ====================

    /**
     * 邮件发送队列
     */
    @Bean
    public Queue emailQueue() {
        return QueueBuilder.durable("email.queue")  // 持久化队列
            .withArgument("x-dead-letter-exchange", "")  // 死信交换机
            .withArgument("x-dead-letter-routing-key", "email.queue.dlq")  // 死信路由键
            .build();
    }

    /**
     * 短信发送队列
     */
    @Bean
    public Queue smsQueue() {
        return QueueBuilder.durable("sms.queue")
            .build();
    }

    /**
     * 订单处理队列(演示延迟队列)
     */
    @Bean
    public Queue orderDelayQueue() {
        return QueueBuilder.durable("order.delay.queue")
            .withArgument("x-dead-letter-exchange", "direct.exchange")  // 死信交换机
            .withArgument("x-dead-letter-routing-key", "order.process")  // 死信路由键
            .withArgument("x-message-ttl", 300000)  // 消息TTL:5分钟
            .build();
    }

    /**
     * 订单处理队列
     */
    @Bean
    public Queue orderProcessQueue() {
        return QueueBuilder.durable("order.process.queue")
            .build();
    }

    // ==================== 绑定关系 ====================

    /**
     * 绑定邮件队列到直连交换机
     */
    @Bean
    public Binding emailBinding() {
        return BindingBuilder
            .bind(emailQueue())
            .to(directExchange())
            .with("email.send");  // Routing Key
    }

    /**
     * 绑定短信队列到直连交换机
     */
    @Bean
    public Binding smsBinding() {
        return BindingBuilder
            .bind(smsQueue())
            .to(directExchange())
            .with("sms.send");
    }

    /**
     * 绑定订单处理队列
     */
    @Bean
    public Binding orderProcessBinding() {
        return BindingBuilder
            .bind(orderProcessQueue())
            .to(directExchange())
            .with("order.process");
    }

    // ==================== 消息转换器 ====================

    /**
     * 配置JSON消息转换器
     * 
     * 将Java对象序列化为JSON格式发送
     * 接收时自动反序列化为Java对象
     */
    @Bean
    public Jackson2JsonMessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    /**
     * 配置RabbitTemplate
     * 
     * RabbitTemplate是Spring提供的RabbitMQ操作模板
     */
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate(connectionFactory);
        template.setMessageConverter(messageConverter());
        
        // 设置确认回调(消息是否成功发送到交换机)
        template.setConfirmCallback((correlationData, ack, cause) -> {
            if (ack) {
                log.debug("消息成功发送到交换机: {}", correlationData);
            } else {
                log.error("消息发送到交换机失败: {}, 原因: {}", correlationData, cause);
            }
        });
        
        // 设置返回回调(消息是否成功路由到队列)
        template.setReturnsCallback(returned -> {
            log.error("消息路由到队列失败: {}, 原因: {}", 
                returned.getMessage(), returned.getReplyText());
        });
        
        return template;
    }
}

创建 micro-common/src/main/java/com/example/common/util/RabbitMQUtil.java

package com.example.common.util;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;

/**
 * RabbitMQ工具类
 * 
 * 封装了常用的消息发送操作
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class RabbitMQUtil {

    private final RabbitTemplate rabbitTemplate;

    /**
     * 发送消息到指定交换机
     * 
     * @param exchange 交换机名称
     * @param routingKey 路由键
     * @param message 消息内容
     */
    public void send(String exchange, String routingKey, Object message) {
        try {
            rabbitTemplate.convertAndSend(exchange, routingKey, message);
            log.debug("消息发送成功: exchange={}, routingKey={}", exchange, routingKey);
        } catch (Exception e) {
            log.error("消息发送失败: exchange={}, routingKey={}, error={}", 
                exchange, routingKey, e.getMessage());
            throw e;
        }
    }

    /**
     * 发送延迟消息
     * 
     * @param exchange 交换机名称
     * @param routingKey 路由键
     * @param message 消息内容
     * @param delayMillis 延迟时间(毫秒)
     */
    public void sendDelay(String exchange, String routingKey, Object message, long delayMillis) {
        try {
            rabbitTemplate.convertAndSend(exchange, routingKey, message, msg -> {
                // 设置消息过期时间(TTL)
                msg.getMessageProperties().setExpiration(String.valueOf(delayMillis));
                return msg;
            });
            log.debug("延迟消息发送成功: exchange={}, routingKey={}, delay={}ms", 
                exchange, routingKey, delayMillis);
        } catch (Exception e) {
            log.error("延迟消息发送失败: {}", e.getMessage());
            throw e;
        }
    }

    /**
     * 发送直连消息(发送到默认直连交换机)
     * 
     * @param routingKey 路由键
     * @param message 消息内容
     */
    public void sendDirect(String routingKey, Object message) {
        send("direct.exchange", routingKey, message);
    }

    /**
     * 发送广播消息
     * 
     * @param exchange 扇形交换机名称
     * @param message 消息内容
     */
    public void sendFanout(String exchange, Object message) {
        // 扇形交换机忽略routingKey
        send(exchange, "", message);
    }
}

4.4 MinIO 集成

创建 micro-common/src/main/java/com/example/common/config/MinioConfig.java

package com.example.common.config;

import io.minio.MinioClient;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * MinIO 配置类
 * 
 * MinIO是一个高性能的对象存储系统,兼容Amazon S3 API
 * 适用于:文件存储、图片存储、视频存储等
 */
@Slf4j
@Configuration
@ConfigurationProperties(prefix = "minio")  // 从配置文件读取minio.*属性
@Data
public class MinioConfig {

    /**
     * MinIO服务地址
     * 示例:http://localhost:9000
     */
    private String endpoint;

    /**
     * 访问密钥(Access Key)
     */
    private String accessKey;

    /**
     * 秘密密钥(Secret Key)
     */
    private String secretKey;

    /**
     * 默认存储桶名称
     */
    private String bucketName = "default";

    /**
     * 创建MinIO客户端
     * 
     * MinioClient是操作MinIO的核心类
     */
    @Bean
    public MinioClient minioClient() {
        log.info("=== 初始化MinIO客户端: {} ===", endpoint);
        
        return MinioClient.builder()
            .endpoint(endpoint)
            .credentials(accessKey, secretKey)
            .build();
    }
}

创建 micro-common/src/main/java/com/example/common/util/MinioUtil.java

package com.example.common.util;

import io.minio.*;
import io.minio.http.Method;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.concurrent.TimeUnit;

/**
 * MinIO工具类
 * 
 * 封装了常用的对象存储操作
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class MinioUtil {

    private final MinioClient minioClient;

    /**
     * 创建存储桶
     * 
     * @param bucketName 存储桶名称
     */
    public void createBucket(String bucketName) {
        try {
            // 检查存储桶是否存在
            boolean exists = minioClient.bucketExists(
                BucketExistsArgs.builder().bucket(bucketName).build()
            );
            
            if (!exists) {
                // 创建存储桶
                minioClient.makeBucket(
                    MakeBucketArgs.builder().bucket(bucketName).build()
                );
                log.info("存储桶创建成功: {}", bucketName);
            }
        } catch (Exception e) {
            log.error("存储桶创建失败: {}, error: {}", bucketName, e.getMessage());
            throw new RuntimeException("存储桶创建失败", e);
        }
    }

    /**
     * 上传文件
     * 
     * @param bucketName 存储桶名称
     * @param objectName 对象名称(文件路径)
     * @param file 上传的文件
     * @return 文件访问URL
     */
    public String uploadFile(String bucketName, String objectName, MultipartFile file) {
        try {
            // 确保存储桶存在
            createBucket(bucketName);
            
            // 上传文件
            minioClient.putObject(
                PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .contentType(file.getContentType())
                    .stream(file.getInputStream(), file.getSize(), -1)
                    .build()
            );
            
            log.info("文件上传成功: {}/{}", bucketName, objectName);
            
            // 返回文件URL
            return getFileUrl(bucketName, objectName);
        } catch (Exception e) {
            log.error("文件上传失败: {}, error: {}", objectName, e.getMessage());
            throw new RuntimeException("文件上传失败", e);
        }
    }

    /**
     * 上传文件(使用流)
     * 
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @param inputStream 输入流
     * @param contentType 内容类型
     * @param size 文件大小
     * @return 文件访问URL
     */
    public String uploadFile(String bucketName, String objectName, 
                            InputStream inputStream, String contentType, long size) {
        try {
            createBucket(bucketName);
            
            minioClient.putObject(
                PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .contentType(contentType)
                    .stream(inputStream, size, -1)
                    .build()
            );
            
            return getFileUrl(bucketName, objectName);
        } catch (Exception e) {
            log.error("文件上传失败: {}", e.getMessage());
            throw new RuntimeException("文件上传失败", e);
        }
    }

    /**
     * 获取文件URL
     * 
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @return 文件URL
     */
    public String getFileUrl(String bucketName, String objectName) {
        try {
            // 生成预签名URL,有效期7天
            return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(bucketName)
                    .object(objectName)
                    .expiry(7, TimeUnit.DAYS)
                    .build()
            );
        } catch (Exception e) {
            log.error("获取文件URL失败: {}", e.getMessage());
            throw new RuntimeException("获取文件URL失败", e);
        }
    }

    /**
     * 下载文件
     * 
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @return 输入流
     */
    public InputStream downloadFile(String bucketName, String objectName) {
        try {
            return minioClient.getObject(
                GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build()
            );
        } catch (Exception e) {
            log.error("文件下载失败: {}", e.getMessage());
            throw new RuntimeException("文件下载失败", e);
        }
    }

    /**
     * 删除文件
     * 
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     */
    public void deleteFile(String bucketName, String objectName) {
        try {
            minioClient.removeObject(
                RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build()
            );
            log.info("文件删除成功: {}/{}", bucketName, objectName);
        } catch (Exception e) {
            log.error("文件删除失败: {}", e.getMessage());
            throw new RuntimeException("文件删除失败", e);
        }
    }

    /**
     * 生成上传URL(前端直传)
     * 
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @return 预签名上传URL
     */
    public String getUploadUrl(String bucketName, String objectName) {
        try {
            createBucket(bucketName);
            
            return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                    .method(Method.PUT)
                    .bucket(bucketName)
                    .object(objectName)
                    .expiry(1, TimeUnit.HOURS)  // URL有效期1小时
                    .build()
            );
        } catch (Exception e) {
            log.error("生成上传URL失败: {}", e.getMessage());
            throw new RuntimeException("生成上传URL失败", e);
        }
    }
}

5. 工具类和配置类

5.1 统一响应结果

创建 micro-common/src/main/java/com/example/common/result/Result.java

package com.example.common.result;

import lombok.Data;

import java.io.Serializable;

/**
 * 统一响应结果封装
 * 
 * 所有API接口都返回此对象,保证响应格式统一
 * 
 * @param <T> 数据类型
 */
@Data
public class Result<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 状态码
     * 200: 成功
     * 其他: 错误码
     */
    private Integer code;

    /**
     * 提示消息
     */
    private String message;

    /**
     * 响应数据
     */
    private T data;

    /**
     * 时间戳
     */
    private Long timestamp;

    public Result() {
        this.timestamp = System.currentTimeMillis();
    }

    // ==================== 成功响应 ====================

    /**
     * 成功响应(无数据)
     */
    public static <T> Result<T> success() {
        Result<T> result = new Result<>();
        result.setCode(ResultCode.SUCCESS.getCode());
        result.setMessage(ResultCode.SUCCESS.getMessage());
        return result;
    }

    /**
     * 成功响应(有数据)
     */
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.setCode(ResultCode.SUCCESS.getCode());
        result.setMessage(ResultCode.SUCCESS.getMessage());
        result.setData(data);
        return result;
    }

    /**
     * 成功响应(自定义消息)
     */
    public static <T> Result<T> success(String message, T data) {
        Result<T> result = new Result<>();
        result.setCode(ResultCode.SUCCESS.getCode());
        result.setMessage(message);
        result.setData(data);
        return result;
    }

    // ==================== 错误响应 ====================

    /**
     * 错误响应
     */
    public static <T> Result<T> error(String message) {
        Result<T> result = new Result<>();
        result.setCode(ResultCode.ERROR.getCode());
        result.setMessage(message);
        return result;
    }

    /**
     * 错误响应(带状态码)
     */
    public static <T> Result<T> error(Integer code, String message) {
        Result<T> result = new Result<>();
        result.setCode(code);
        result.setMessage(message);
        return result;
    }

    /**
     * 错误响应(使用ResultCode)
     */
    public static <T> Result<T> error(ResultCode resultCode) {
        Result<T> result = new Result<>();
        result.setCode(resultCode.getCode());
        result.setMessage(resultCode.getMessage());
        return result;
    }
}

创建 micro-common/src/main/java/com/example/common/result/ResultCode.java

package com.example.common.result;

import lombok.Getter;

/**
 * 响应状态码枚举
 */
@Getter
public enum ResultCode {

    // 成功
    SUCCESS(200, "操作成功"),
    
    // 客户端错误 1xxx
    PARAM_ERROR(1001, "参数错误"),
    PARAM_EMPTY(1002, "参数为空"),
    PARAM_TYPE_ERROR(1003, "参数类型错误"),
    PARAM_FORMAT_ERROR(1004, "参数格式错误"),
    
    // 认证授权 2xxx
    UNAUTHORIZED(2001, "未登录或登录已过期"),
    FORBIDDEN(2002, "没有操作权限"),
    TOKEN_INVALID(2003, "Token无效"),
    TOKEN_EXPIRED(2004, "Token已过期"),
    
    // 业务错误 3xxx
    USER_NOT_EXIST(3001, "用户不存在"),
    USER_ALREADY_EXIST(3002, "用户已存在"),
    PASSWORD_ERROR(3003, "密码错误"),
    ACCOUNT_DISABLED(3004, "账号已被禁用"),
    
    // 系统错误 5xxx
    ERROR(5000, "系统错误"),
    SERVER_BUSY(5001, "服务器繁忙"),
    DATABASE_ERROR(5002, "数据库操作失败"),
    CACHE_ERROR(5003, "缓存操作失败"),
    MQ_ERROR(5004, "消息队列操作失败"),
    FILE_ERROR(5005, "文件操作失败");

    private final Integer code;
    private final String message;

    ResultCode(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}

5.2 分页结果封装

创建 micro-common/src/main/java/com/example/common/result/PageResult.java

package com.example.common.result;

import com.mybatisflex.core.paginate.Page;
import lombok.Data;

import java.io.Serializable;
import java.util.List;

/**
 * 分页结果封装
 * 
 * @param <T> 数据类型
 */
@Data
public class PageResult<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 当前页码
     */
    private Long pageNum;

    /**
     * 每页大小
     */
    private Long pageSize;

    /**
     * 总记录数
     */
    private Long total;

    /**
     * 总页数
     */
    private Long pages;

    /**
     * 数据列表
     */
    private List<T> list;

    /**
     * 是否有下一页
     */
    private Boolean hasNext;

    /**
     * 是否有上一页
     */
    private Boolean hasPrevious;

    /**
     * 从MyBatis-Flex Page转换
     * 
     * @param page MyBatis-Flex分页对象
     * @return 分页结果
     */
    public static <T> PageResult<T> of(Page<T> page) {
        PageResult<T> result = new PageResult<>();
        result.setPageNum(page.getPageNumber());
        result.setPageSize(page.getPageSize());
        result.setTotal(page.getTotalRow());
        result.setPages(page.getTotalPage());
        result.setList(page.getRecords());
        result.setHasNext(page.getPageNumber() < page.getTotalPage());
        result.setHasPrevious(page.getPageNumber() > 1);
        return result;
    }
}

5.3 全局异常处理

创建 micro-common/src/main/java/com/example/common/exception/GlobalExceptionHandler.java

package com.example.common.exception;

import com.example.common.result.Result;
import com.example.common.result.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import java.util.stream.Collectors;

/**
 * 全局异常处理器
 * 
 * @RestControllerAdvice: 标记为全局异常处理类
 * 作用:捕获Controller层抛出的所有异常,统一处理并返回标准响应
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理业务异常
     * 
     * @param e 业务异常
     * @return 错误响应
     */
    @ExceptionHandler(BusinessException.class)
    public Result<Void> handleBusinessException(BusinessException e) {
        log.warn("业务异常: {}", e.getMessage());
        return Result.error(e.getCode(), e.getMessage());
    }

    /**
     * 处理参数校验异常(@Valid)
     * 
     * @param e 参数校验异常
     * @return 错误响应
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<Void> handleValidationException(MethodArgumentNotValidException e) {
        // 获取所有校验错误信息
        String message = e.getBindingResult().getFieldErrors().stream()
            .map(FieldError::getDefaultMessage)
            .collect(Collectors.joining(", "));
        
        log.warn("参数校验失败: {}", message);
        return Result.error(ResultCode.PARAM_ERROR.getCode(), message);
    }

    /**
     * 处理参数绑定异常
     */
    @ExceptionHandler(BindException.class)
    public Result<Void> handleBindException(BindException e) {
        String message = e.getFieldErrors().stream()
            .map(FieldError::getDefaultMessage)
            .collect(Collectors.joining(", "));
        
        log.warn("参数绑定失败: {}", message);
        return Result.error(ResultCode.PARAM_ERROR.getCode(), message);
    }

    /**
     * 处理其他所有异常
     * 
     * @param e 异常
     * @param request 请求对象
     * @return 错误响应
     */
    @ExceptionHandler(Exception.class)
    public Result<Void> handleException(Exception e, HttpServletRequest request) {
        log.error("系统异常: {}, URL: {}", e.getMessage(), request.getRequestURI(), e);
        return Result.error(ResultCode.ERROR);
    }
}

创建 micro-common/src/main/java/com/example/common/exception/BusinessException.java

package com.example.common.exception;

import com.example.common.result.ResultCode;
import lombok.Getter;

/**
 * 业务异常
 * 
 * 用于抛出业务逻辑错误,会被GlobalExceptionHandler捕获处理
 */
@Getter
public class BusinessException extends RuntimeException {

    private final Integer code;

    public BusinessException(String message) {
        super(message);
        this.code = ResultCode.ERROR.getCode();
    }

    public BusinessException(ResultCode resultCode) {
        super(resultCode.getMessage());
        this.code = resultCode.getCode();
    }

    public BusinessException(Integer code, String message) {
        super(message);
        this.code = code;
    }
}

5.4 JWT工具类

创建 micro-common/src/main/java/com/example/common/util/JwtUtil.java

package com.example.common.util;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT工具类
 * 
 * JWT(JSON Web Token)用于用户认证和信息传递
 * 结构:Header.Payload.Signature
 */
@Slf4j
@Component
public class JwtUtil {

    /**
     * 密钥,从配置文件读取
     */
    @Value("${jwt.secret:your-256-bit-secret-your-256-bit-secret}")
    private String secret;

    /**
     * 过期时间(毫秒),默认7天
     */
    @Value("${jwt.expiration:604800000}")
    private Long expiration;

    /**
     * 生成密钥
     */
    private SecretKey getKey() {
        return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 生成JWT Token
     * 
     * @param userId 用户ID
     * @param username 用户名
     * @return JWT字符串
     */
    public String generateToken(Long userId, String username) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userId);
        claims.put("username", username);

        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + expiration);

        return Jwts.builder()
            .setClaims(claims)                    // 自定义声明
            .setSubject(String.valueOf(userId))   // 主题(用户ID)
            .setIssuedAt(now)                     // 签发时间
            .setExpiration(expiryDate)            // 过期时间
            .signWith(getKey(), SignatureAlgorithm.HS256)  // 签名算法
            .compact();
    }

    /**
     * 从Token解析Claims
     * 
     * @param token JWT字符串
     * @return Claims对象
     */
    public Claims parseToken(String token) {
        try {
            return Jwts.parserBuilder()
                .setSigningKey(getKey())
                .build()
                .parseClaimsJws(token)
                .getBody();
        } catch (ExpiredJwtException e) {
            log.warn("Token已过期");
            throw e;
        } catch (JwtException e) {
            log.warn("Token解析失败: {}", e.getMessage());
            throw e;
        }
    }

    /**
     * 验证Token是否有效
     * 
     * @param token JWT字符串
     * @return 是否有效
     */
    public boolean validateToken(String token) {
        try {
            parseToken(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 从Token获取用户ID
     * 
     * @param token JWT字符串
     * @return 用户ID
     */
    public Long getUserIdFromToken(String token) {
        Claims claims = parseToken(token);
        return Long.valueOf(claims.get("userId").toString());
    }

    /**
     * 从Token获取用户名
     * 
     * @param token JWT字符串
     * @return 用户名
     */
    public String getUsernameFromToken(String token) {
        Claims claims = parseToken(token);
        return claims.get("username").toString();
    }

    /**
     * 判断Token是否过期
     * 
     * @param token JWT字符串
     * @return 是否过期
     */
    public boolean isTokenExpired(String token) {
        try {
            Claims claims = parseToken(token);
            return claims.getExpiration().before(new Date());
        } catch (ExpiredJwtException e) {
            return true;
        }
    }
}

6. 部署和运维

6.1 多环境配置文件

创建 micro-service-user/src/main/resources/application.yml

# 主配置文件
# 用于指定激活的环境和加载Nacos配置

spring:
  application:
    name: micro-service-user
  profiles:
    active: dev  # 激活dev环境
  config:
    import:
      # 从Nacos加载配置
      - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml
      - optional:nacos:common.yaml

  cloud:
    nacos:
      server-addr: ${NACOS_SERVER:127.0.0.1:8848}
      username: ${NACOS_USERNAME:nacos}
      password: ${NACOS_PASSWORD:nacos}
      config:
        namespace: ${NACOS_NAMESPACE:dev}
        group: DEFAULT_GROUP
        file-extension: yaml
      discovery:
        namespace: ${NACOS_NAMESPACE:dev}
        group: DEFAULT_GROUP
        metadata:
          version: 1.0.0
          region: default

创建 micro-service-user/src/main/resources/application-dev.yml

# 开发环境配置
# 本地开发使用,连接本地或Docker部署的服务

server:
  port: 9001

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/micro_service?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: root123456
    druid:
      initial-size: 5
      min-idle: 5
      max-active: 20
      max-wait: 60000

  redis:
    host: localhost
    port: 6379
    password: redis123456
    database: 0
    timeout: 10s
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0

  rabbitmq:
    host: localhost
    port: 5672
    username: admin
    password: admin123456
    virtual-host: /
    listener:
      simple:
        acknowledge-mode: manual
        concurrency: 5
        max-concurrency: 10

minio:
  endpoint: http://localhost:9000
  access-key: minioadmin
  secret-key: minioadmin123
  bucket-name: micro-service

# 日志配置
logging:
  level:
    root: INFO
    com.example: DEBUG
    com.mybatisflex: DEBUG
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"

创建 micro-service-user/src/main/resources/application-prod.yml

# 生产环境配置
# 生产环境使用,使用环境变量注入敏感信息

server:
  port: ${SERVER_PORT:9001}

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: ${MYSQL_URL:jdbc:mysql://mysql:3306/micro_service?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai}
    username: ${MYSQL_USERNAME:root}
    password: ${MYSQL_PASSWORD}
    druid:
      initial-size: 10
      min-idle: 10
      max-active: 100
      max-wait: 60000
      test-on-borrow: false
      test-while-idle: true

  redis:
    host: ${REDIS_HOST:redis}
    port: ${REDIS_PORT:6379}
    password: ${REDIS_PASSWORD}
    database: 0
    timeout: 30s
    lettuce:
      pool:
        max-active: 50
        max-idle: 50
        min-idle: 10

  rabbitmq:
    host: ${RABBITMQ_HOST:rabbitmq}
    port: ${RABBITMQ_PORT:5672}
    username: ${RABBITMQ_USERNAME:admin}
    password: ${RABBITMQ_PASSWORD}
    virtual-host: /
    listener:
      simple:
        acknowledge-mode: manual
        concurrency: 10
        max-concurrency: 50

minio:
  endpoint: ${MINIO_ENDPOINT:http://minio:9000}
  access-key: ${MINIO_ACCESS_KEY}
  secret-key: ${MINIO_SECRET_KEY}
  bucket-name: ${MINIO_BUCKET:micro-service}

# 生产环境日志
logging:
  level:
    root: WARN
    com.example: INFO
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
  file:
    name: /var/log/micro-service/app.log

6.2 Dockerfile

创建 micro-service-user/Dockerfile

# ============================================
# Spring Boot 服务 Dockerfile
# 使用多阶段构建,减小镜像体积
# ============================================

# 第一阶段:构建阶段
FROM eclipse-temurin:17-jdk-alpine AS builder

# 设置工作目录
WORKDIR /app

# 复制Maven配置
COPY pom.xml .
COPY micro-common/pom.xml micro-common/
COPY micro-service-user/pom.xml micro-service-user/

# 下载依赖(利用Docker缓存层)
RUN mkdir -p micro-common/src micro-service-user/src
RUN --mount=type=cache,target=/root/.m2 \
    mvn dependency:go-offline -B

# 复制源代码
COPY micro-common/src micro-common/src
COPY micro-service-user/src micro-service-user/src

# 构建项目
RUN --mount=type=cache,target=/root/.m2 \
    mvn clean package -DskipTests -pl micro-service-user -am

# 第二阶段:运行阶段
FROM eclipse-temurin:17-jre-alpine

# 安装常用工具
RUN apk add --no-cache curl tzdata

# 设置时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# 创建非root用户(安全最佳实践)
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# 设置工作目录
WORKDIR /app

# 从构建阶段复制JAR文件
COPY --from=builder /app/micro-service-user/target/*.jar app.jar

# 创建日志目录
RUN mkdir -p /var/log/micro-service && chown -R appuser:appgroup /var/log/micro-service

# 切换到非root用户
USER appuser

# 暴露端口
EXPOSE 9001

# JVM参数(可通过环境变量覆盖)
ENV JAVA_OPTS="-Xms512m -Xmx512m -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError"

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
    CMD curl -f http://localhost:9001/actuator/health || exit 1

# 启动命令
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

创建 micro-gateway/Dockerfile

# 网关服务 Dockerfile
FROM eclipse-temurin:17-jdk-alpine AS builder

WORKDIR /app
COPY pom.xml .
COPY micro-gateway/pom.xml micro-gateway/
RUN mkdir -p micro-gateway/src
RUN --mount=type=cache,target=/root/.m2 mvn dependency:go-offline -B

COPY micro-gateway/src micro-gateway/src
RUN --mount=type=cache,target=/root/.m2 mvn clean package -DskipTests -pl micro-gateway

FROM eclipse-temurin:17-jre-alpine
RUN apk add --no-cache curl tzdata
ENV TZ=Asia/Shanghai
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app
COPY --from=builder /app/micro-gateway/target/*.jar app.jar
USER appuser

EXPOSE 8080
ENV JAVA_OPTS="-Xms256m -Xmx256m"

HEALTHCHECK --interval=30s --timeout=3s --start-period=30s --retries=3 \
    CMD curl -f http://localhost:8080/actuator/health || exit 1

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

6.3 一键启动脚本

创建 start.sh

#!/bin/bash

# ============================================
# 微服务一键启动脚本
# 支持:本地启动、Docker启动、生产部署
# ============================================

set -e  # 遇到错误立即退出

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# 打印带颜色的信息
print_info() {
    echo -e "${GREEN}[INFO]${NC} $1"
}

print_warn() {
    echo -e "${YELLOW}[WARN]${NC} $1"
}

print_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

# 帮助信息
show_help() {
    cat << EOF
微服务一键启动脚本

用法: ./start.sh [选项] [命令]

选项:
    -h, --help          显示帮助信息
    -e, --env           指定环境 (dev|test|prod),默认: dev
    -m, --mode          启动模式 (local|docker|build),默认: local

命令:
    start               启动服务
    stop                停止服务
    restart             重启服务
    status              查看服务状态
    logs                查看日志
    clean               清理环境

示例:
    ./start.sh start                    # 本地开发环境启动
    ./start.sh -e prod -m docker start  # Docker生产环境启动
    ./start.sh stop                     # 停止服务
    ./start.sh logs gateway             # 查看网关日志

EOF
}

# 默认参数
ENV="dev"
MODE="local"
COMMAND=""

# 解析参数
while [[ $# -gt 0 ]]; do
    case $1 in
        -h|--help)
            show_help
            exit 0
            ;;
        -e|--env)
            ENV="$2"
            shift 2
            ;;
        -m|--mode)
            MODE="$2"
            shift 2
            ;;
        start|stop|restart|status|logs|clean)
            COMMAND="$1"
            shift
            ;;
        *)
            print_error "未知参数: $1"
            show_help
            exit 1
            ;;
    esac
done

# 检查命令
if [ -z "$COMMAND" ]; then
    print_error "请指定命令"
    show_help
    exit 1
fi

print_info "环境: $ENV, 模式: $MODE, 命令: $COMMAND"

# ============================================
# 本地模式
# ============================================
start_local() {
    print_info "启动基础设施服务..."
    
    # 检查Docker是否运行
    if ! docker info > /dev/null 2>&1; then
        print_error "Docker未运行,请先启动Docker"
        exit 1
    fi
    
    # 启动基础设施
    cd infrastructure
    docker-compose up -d
    cd ..
    
    print_info "等待服务启动..."
    sleep 10
    
    # 检查服务健康状态
    check_services
    
    print_info "启动微服务..."
    
    # 编译并启动网关
    print_info "启动网关服务..."
    cd micro-gateway
    mvn spring-boot:run -Dspring-boot.run.profiles=$ENV &
    cd ..
    
    # 编译并启动用户服务
    print_info "启动用户服务..."
    cd micro-service-user
    mvn spring-boot:run -Dspring-boot.run.profiles=$ENV &
    cd ..
    
    print_info "所有服务启动完成!"
    print_info "网关地址: http://localhost:8080"
    print_info "用户服务: http://localhost:9001"
}

stop_local() {
    print_info "停止微服务..."
    
    # 停止Java进程
    pkill -f "micro-gateway" || true
    pkill -f "micro-service-user" || true
    
    # 停止基础设施
    cd infrastructure
    docker-compose down
    cd ..
    
    print_info "服务已停止"
}

# ============================================
# Docker模式
# ============================================
start_docker() {
    print_info "Docker模式启动..."
    
    # 构建镜像
    print_info "构建Docker镜像..."
    docker build -t micro-gateway:latest -f micro-gateway/Dockerfile .
    docker build -t micro-service-user:latest -f micro-service-user/Dockerfile .
    
    # 启动所有服务
    docker-compose -f docker-compose.yml -f docker-compose.$ENV.yml up -d
    
    print_info "服务启动完成!"
    docker-compose ps
}

stop_docker() {
    print_info "停止Docker服务..."
    docker-compose down
}

# ============================================
# 通用函数
# ============================================
check_services() {
    print_info "检查服务健康状态..."
    
    services=("http://localhost:8848/nacos" "http://localhost:15672" "http://localhost:9001")
    names=("Nacos" "RabbitMQ" "MinIO")
    
    for i in "${!services[@]}"; do
        if curl -s "${services[$i]}" > /dev/null; then
            print_info "${names[$i]} 运行正常"
        else
            print_warn "${names[$i]} 可能未就绪,继续等待..."
        fi
    done
}

show_status() {
    print_info "服务状态:"
    
    if [ "$MODE" = "docker" ]; then
        docker-compose ps
    else
        # 检查Java进程
        pgrep -f "micro-gateway" > /dev/null && print_info "网关服务: 运行中" || print_warn "网关服务: 未运行"
        pgrep -f "micro-service-user" > /dev/null && print_info "用户服务: 运行中" || print_warn "用户服务: 未运行"
        
        # 检查Docker容器
        cd infrastructure
        docker-compose ps
        cd ..
    fi
}

show_logs() {
    SERVICE=${1:-all}
    
    if [ "$MODE" = "docker" ]; then
        if [ "$SERVICE" = "all" ]; then
            docker-compose logs -f
        else
            docker-compose logs -f $SERVICE
        fi
    else
        print_warn "本地模式请查看各服务的控制台输出"
    fi
}

clean_environment() {
    print_warn "清理所有数据,此操作不可恢复!"
    read -p "确认继续? (y/N) " -n 1 -r
    echo
    if [[ $REPLY =~ ^[Yy]$ ]]; then
        stop_local
        rm -rf infrastructure/mysql/data
        rm -rf infrastructure/redis/data
        rm -rf infrastructure/rabbitmq/data
        rm -rf infrastructure/minio/data
        print_info "环境已清理"
    else
        print_info "取消清理"
    fi
}

# ============================================
# 主逻辑
# ============================================
case $COMMAND in
    start)
        if [ "$MODE" = "docker" ]; then
            start_docker
        else
            start_local
        fi
        ;;
    stop)
        if [ "$MODE" = "docker" ]; then
            stop_docker
        else
            stop_local
        fi
        ;;
    restart)
        $0 stop
        sleep 2
        $0 start
        ;;
    status)
        show_status
        ;;
    logs)
        show_logs $2
        ;;
    clean)
        clean_environment
        ;;
    *)
        print_error "未知命令: $COMMAND"
        show_help
        exit 1
        ;;
esac

赋予执行权限:

chmod +x start.sh

6.4 Docker Compose 生产配置

创建 docker-compose.yml

version: '3.8'

services:
  # 网关服务
  gateway:
    image: micro-gateway:latest
    container_name: micro-gateway
    restart: always
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - NACOS_SERVER=nacos:8848
      - JAVA_OPTS=-Xms256m -Xmx256m
    depends_on:
      - nacos
    networks:
      - micro-service-network

  # 用户服务
  user-service:
    image: micro-service-user:latest
    container_name: micro-service-user
    restart: always
    ports:
      - "9001:9001"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - NACOS_SERVER=nacos:8848
      - MYSQL_URL=jdbc:mysql://mysql:3306/micro_service?useUnicode=true&characterEncoding=utf-8&useSSL=false
      - MYSQL_USERNAME=root
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - REDIS_HOST=redis
      - REDIS_PASSWORD=${REDIS_PASSWORD}
      - RABBITMQ_HOST=rabbitmq
      - RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD}
      - MINIO_ENDPOINT=http://minio:9000
      - MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}
      - MINIO_SECRET_KEY=${MINIO_SECRET_KEY}
      - JAVA_OPTS=-Xms512m -Xmx512m
    depends_on:
      - mysql
      - redis
      - rabbitmq
      - minio
      - nacos
    networks:
      - micro-service-network

networks:
  micro-service-network:
    external: true  # 使用外部网络,与基础设施共享

7. 完整项目结构

micro-service-parent/                          # 父工程
├── pom.xml                                    # 父POM,统一管理依赖版本
├── start.sh                                   # 一键启动脚本
├── docker-compose.yml                         # Docker Compose生产配置
├── README.md                                  # 项目说明文档
│
├── infrastructure/                            # 基础设施Docker Compose
│   ├── docker-compose.yml                     # MySQL、Redis、RabbitMQ、MinIO、Nacos
│   ├── mysql/
│   │   ├── init/                              # 初始化SQL脚本
│   │   └── conf/                              # MySQL配置文件
│   └── redis/
│       └── redis.conf                         # Redis配置文件
│
├── micro-common/                              # 公共模块
│   ├── pom.xml
│   └── src/main/java/com/example/common/
│       ├── config/                            # 配置类
│       │   ├── MybatisFlexConfig.java
│       │   ├── RedisConfig.java
│       │   ├── RabbitMQConfig.java
│       │   └── MinioConfig.java
│       ├── entity/                            # 基础实体
│       │   └── BaseEntity.java
│       ├── result/                            # 响应结果封装
│       │   ├── Result.java
│       │   ├── ResultCode.java
│       │   └── PageResult.java
│       ├── exception/                         # 异常处理
│       │   ├── GlobalExceptionHandler.java
│       │   └── BusinessException.java
│       └── util/                              # 工具类
│           ├── RedisUtil.java
│           ├── RabbitMQUtil.java
│           ├── MinioUtil.java
│           └── JwtUtil.java
│
├── micro-gateway/                             # 网关服务
│   ├── pom.xml
│   ├── Dockerfile
│   └── src/main/java/com/example/gateway/
│       └── GatewayApplication.java
│   └── src/main/resources/
│       ├── application.yml
│       └── bootstrap.yml
│
└── micro-service-user/                        # 用户服务(示例业务服务)
    ├── pom.xml
    ├── Dockerfile
    └── src/main/java/com/example/user/
        ├── UserServiceApplication.java
        ├── controller/
        ├── service/
        ├── mapper/
        └── entity/
    └── src/main/resources/
        ├── application.yml
        ├── application-dev.yml
        └── application-prod.yml

快速开始指南

1. 克隆项目并启动

# 克隆项目
git clone http://localhost:9090/git/student_study.git
cd student_study

# 启动所有服务(开发环境)
./start.sh start

# 或启动所有服务(Docker模式)
./start.sh -m docker start

2. 验证服务

服务

地址

网关

http://localhost:8080

Nacos

http://localhost:8848/nacos (nacos/nacos)

RabbitMQ

http://localhost:15672 (admin/admin123456)

MinIO

http://localhost:9001 (minioadmin/minioadmin123)

3. 开发新服务

  1. 复制 micro-service-user 模块

  2. 修改 pom.xml 中的 artifactId 和 name

  3. 修改 application.yml 中的服务名称

  4. 实现业务代码

  5. 在父POM中添加新模块


总结

本文档详细介绍了从零搭建Spring Boot 3微服务架构的完整过程:

核心技术点

  1. Spring Boot 3 - 使用Java 17,最新特性

  2. Spring Cloud Alibaba - Nacos注册中心/配置中心

  3. Spring Cloud Gateway - 高性能网关

  4. MyBatis-Flex - 新一代ORM框架

  5. Redis - 缓存、分布式锁

  6. RabbitMQ - 消息队列,支持延迟消息

  7. MinIO - 对象存储

  8. Docker - 容器化部署

最佳实践

  • ✅ 多环境配置(dev/test/prod)

  • ✅ 统一响应格式和异常处理

  • ✅ 完善的工具类封装

  • ✅ 一键启动脚本

  • ✅ Docker多阶段构建

  • ✅ 健康检查和日志配置

按照本文档,任何小白都可以搭建出可运行的微服务架构!

评论