Przeglądaj źródła

修改了代码问题,CI新版本

demo-user 2 miesięcy temu
rodzic
commit
921e632cc9

+ 7 - 0
Dockerfile.multiarch

@@ -25,6 +25,13 @@ WORKDIR /build
 COPY pom.xml .
 COPY shop-recycle-common/ shop-recycle-common/
 COPY shop-recycle-gateway/ shop-recycle-gateway/
+COPY shop-recycle-order-service/ shop-recycle-order-service/
+COPY shop-recycle-payment-service/ shop-recycle-payment-service/
+
+# Build common module first and install to local repository
+RUN mvn clean install -pl shop-recycle-common \
+    -DskipTests -B -q \
+    -Dmaven.test.skip=true
 
 RUN mvn dependency:go-offline -B -q
 

+ 330 - 0
Jenkinsfile.optimized

@@ -0,0 +1,330 @@
+// ============================================
+// Jenkins Pipeline for Spring Cloud Log Demo
+// CI (Continuous Integration) Pipeline - 优化版
+// ============================================
+// 优化点:
+// - Docker 层缓存(分离 POM 和源码)
+// - Maven 缓存持久化
+// - 镜像并行构建(3 倍加速)
+// - 智能增量编译
+
+pipeline {
+    agent any
+
+    options {
+        // 保留最近 15 次构建
+        buildDiscarder(logRotator(numToKeepStr: '15'))
+        // 30 分钟超时
+        timeout(time: 30, unit: 'MINUTES')
+        // 禁止并发构建(保护 Maven 缓存)
+        disableConcurrentBuilds()
+        // 时间戳
+        timestamps()
+    }
+
+    parameters {
+        choice(
+            name: 'BUILD_TYPE',
+            choices: ['SNAPSHOT', 'RELEASE'],
+            description: '构建类型'
+        )
+        booleanParam(
+            name: 'SKIP_TESTS',
+            defaultValue: false,
+            description: '跳过单元测试'
+        )
+        booleanParam(
+            name: 'PUSH_DOCKER',
+            defaultValue: false,
+            description: '推送Docker镜像到仓库'
+        )
+        string(
+            name: 'DOCKER_REGISTRY',
+            defaultValue: 'harbor.stardance',
+            description: 'Docker注册表地址'
+        )
+        string(
+            name: 'DOCKER_NAMESPACE',
+            defaultValue: 'shoprecycle',
+            description: 'Docker命名空间'
+        )
+    }
+
+    environment {
+        // ✅ 关键优化:Maven 缓存配置
+        MAVEN_HOME = tool('maven-3.8')
+        MAVEN_CACHE_DIR = "${WORKSPACE}/.m2/repository"
+        MAVEN_OPTS = '''
+            -Dmaven.repo.local=${WORKSPACE}/.m2/repository \
+            -Xmx1024m -Xms512m \
+            -XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
+            -Dorg.slf4j.simpleLogger.defaultLogLevel=warn
+        '''
+        
+        // Git 信息
+        GIT_COMMIT_SHORT = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
+        GIT_AUTHOR = sh(script: 'git log -1 --format=%an', returnStdout: true).trim()
+        
+        // 构建版本
+        BUILD_NUMBER_PADDED = sh(script: 'printf "%04d" ${BUILD_NUMBER}', returnStdout: true).trim()
+        APP_VERSION = "${BUILD_TYPE == 'RELEASE' ? '1.0' : '1.0.0'}-${BUILD_NUMBER_PADDED}"
+        IMAGE_TAG = "${APP_VERSION}-${GIT_COMMIT_SHORT}"
+        
+        // Docker 配置
+        DOCKER_BUILDKIT = '1'
+        DOCKER_DEFAULT_PLATFORM = 'linux/amd64'
+    }
+
+    stages {
+        stage('準備') {
+            steps {
+                script {
+                    echo """
+                    ════════════════════════════════════════════
+                    CI 构建信息(优化版)
+                    ════════════════════════════════════════════
+                    构建类型: ${BUILD_TYPE}
+                    Git 提交: ${GIT_COMMIT_SHORT}
+                    作者: ${GIT_AUTHOR}
+                    应用版本: ${APP_VERSION}
+                    镜像TAG: ${IMAGE_TAG}
+                    跳过测试: ${SKIP_TESTS}
+                    推送Docker: ${PUSH_DOCKER}
+                    ════════════════════════════════════════════
+                    """
+                }
+                
+                // 清理 workspace
+                deleteDir()
+                checkout scm
+                
+                // 显示工具版本和缓存信息
+                sh '''
+                    echo "==== 环境信息 ===="
+                    java -version 2>&1 | head -1
+                    mvn --version | head -1
+                    docker --version
+                    
+                    echo ""
+                    echo "==== 缓存信息 ===="
+                    du -sh ${WORKSPACE}/.m2/repository 2>/dev/null || echo "缓存: 首次构建"
+                '''
+            }
+        }
+
+        stage('代码检查') {
+            steps {
+                script {
+                    echo ">>> 编译 Common 模块..."
+                    sh '''
+                        mvn clean package -pl shop-recycle-common \
+                            -DskipTests \
+                            -B \
+                            -Dmaven.test.skip=true \
+                            || { echo "❌ Common 模块构建失败"; exit 1; }
+                    '''
+                }
+            }
+        }
+
+        stage('编译与测试') {
+            steps {
+                script {
+                    echo ">>> 执行 Maven 编译..."
+                    
+                    def testArgs = SKIP_TESTS == 'true' ? '-DskipTests' : ''
+                    
+                    sh """
+                        mvn package \
+                            ${testArgs} \
+                            -B \
+                            -Dmaven.test.skip=${SKIP_TESTS} \
+                            -Ddocker.build.number=${BUILD_NUMBER_PADDED} \
+                            -Dgit.commit=${GIT_COMMIT_SHORT}
+                    """
+                }
+            }
+            post {
+                always {
+                    // 收集测试结果
+                    junit allowEmptyResults: true, testResults: '**/target/surefire-reports/*.xml'
+                }
+                failure {
+                    script {
+                        echo "❌ 编译失败!"
+                    }
+                }
+            }
+        }
+
+        stage('构建Docker镜像') {
+            // ✅ 关键优化:并行构建 3 个服务
+            parallel {
+                stage('Gateway') {
+                    steps {
+                        script {
+                            echo ">>> 构建 Gateway 镜像: ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/gateway:${IMAGE_TAG}"
+                            sh '''
+                                docker build \
+                                    --build-arg BUILD_NUMBER=${BUILD_NUMBER_PADDED} \
+                                    --build-arg GIT_COMMIT=${GIT_COMMIT_SHORT} \
+                                    --cache-from ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/gateway:latest \
+                                    --cache-from ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/gateway:${APP_VERSION} \
+                                    -t ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/gateway:${IMAGE_TAG} \
+                                    -t ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/gateway:latest \
+                                    -f shop-recycle-gateway/Dockerfile \
+                                    .
+                            '''
+                        }
+                    }
+                }
+                stage('OrderService') {
+                    steps {
+                        script {
+                            echo ">>> 构建 OrderService 镜像: ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/order-service:${IMAGE_TAG}"
+                            sh '''
+                                docker build \
+                                    --build-arg BUILD_NUMBER=${BUILD_NUMBER_PADDED} \
+                                    --build-arg GIT_COMMIT=${GIT_COMMIT_SHORT} \
+                                    --cache-from ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/order-service:latest \
+                                    --cache-from ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/order-service:${APP_VERSION} \
+                                    -t ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/order-service:${IMAGE_TAG} \
+                                    -t ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/order-service:latest \
+                                    -f shop-recycle-order-service/Dockerfile \
+                                    .
+                            '''
+                        }
+                    }
+                }
+                stage('PaymentService') {
+                    steps {
+                        script {
+                            echo ">>> 构建 PaymentService 镜像: ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/payment-service:${IMAGE_TAG}"
+                            sh '''
+                                docker build \
+                                    --build-arg BUILD_NUMBER=${BUILD_NUMBER_PADDED} \
+                                    --build-arg GIT_COMMIT=${GIT_COMMIT_SHORT} \
+                                    --cache-from ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/payment-service:latest \
+                                    --cache-from ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/payment-service:${APP_VERSION} \
+                                    -t ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/payment-service:${IMAGE_TAG} \
+                                    -t ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/payment-service:latest \
+                                    -f shop-recycle-payment-service/Dockerfile \
+                                    .
+                            '''
+                        }
+                    }
+                }
+            }
+        }
+
+        stage('镜像安全扫描') {
+            when {
+                expression { PUSH_DOCKER == 'true' }
+            }
+            steps {
+                script {
+                    echo ">>> 执行镜像安全扫描..."
+                    sh '''
+                        # 使用 trivy 进行镜像扫描(如果安装)
+                        if command -v trivy &> /dev/null; then
+                            echo "扫描镜像漏洞..."
+                            trivy image --severity HIGH,CRITICAL \
+                                ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/gateway:${IMAGE_TAG} || true
+                        else
+                            echo "⚠️ Trivy 未安装,跳过镜像扫描"
+                        fi
+                    '''
+                }
+            }
+        }
+
+        stage('推送Docker镜像') {
+            when {
+                expression { PUSH_DOCKER == 'true' }
+            }
+            steps {
+                script {
+                    echo ">>> 推送 Docker 镜像到 ${DOCKER_REGISTRY}..."
+                    
+                    withCredentials([usernamePassword(
+                        credentialsId: 'docker-registry-credentials',
+                        usernameVariable: 'DOCKER_USER',
+                        passwordVariable: 'DOCKER_PASS'
+                    )]) {
+                        sh '''
+                            echo "${DOCKER_PASS}" | docker login -u "${DOCKER_USER}" --password-stdin ${DOCKER_REGISTRY}
+                            
+                            # ✅ 优化:并行推送镜像(快 3 倍)
+                            docker push ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/gateway:${IMAGE_TAG} &
+                            docker push ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/gateway:latest &
+                            
+                            docker push ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/order-service:${IMAGE_TAG} &
+                            docker push ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/order-service:latest &
+                            
+                            docker push ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/payment-service:${IMAGE_TAG} &
+                            docker push ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/payment-service:latest &
+                            
+                            wait
+                            
+                            docker logout ${DOCKER_REGISTRY}
+                            
+                            echo "✅ 镜像推送完成"
+                        '''
+                    }
+                }
+            }
+        }
+    }
+
+    post {
+        always {
+            script {
+                echo ">>> 清理与报告..."
+                
+                // 收集构建工件
+                archiveArtifacts artifacts: '**/target/*.jar', 
+                                 allowEmptyArchive: true,
+                                 fingerprint: true
+                
+                sh '''
+                    echo "═══════════════════════════════════════"
+                    echo "CI 构建完成"
+                    echo "═══════════════════════════════════════"
+                    echo "镜像TAG: ${IMAGE_TAG}"
+                    docker images | grep shoprecycle | head -6 || true
+                    
+                    echo ""
+                    echo "缓存大小:"
+                    du -sh ${WORKSPACE}/.m2/repository 2>/dev/null || echo "Unknown"
+                '''
+            }
+        }
+        success {
+            script {
+                echo "✅ CI 构建成功!"
+                echo "镜像已准备就绪:"
+                echo "  - ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/gateway:${IMAGE_TAG}"
+                echo "  - ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/order-service:${IMAGE_TAG}"
+                echo "  - ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/payment-service:${IMAGE_TAG}"
+            }
+        }
+        failure {
+            script {
+                echo "❌ CI 构建失败,请检查日志"
+                sh '''
+                    # 清理失败的镜像
+                    docker images | grep shoprecycle | grep -E "^<none>" | awk '{print $3}' | xargs -r docker rmi -f || true
+                '''
+            }
+        }
+        cleanup {
+            sh '''
+                # ✅ 保留 Maven 缓存用于下次构建(关键优化)
+                echo "缓存已保留,下次构建会使用"
+                
+                # 清理 1 周前的临时文件(可选)
+                find ${WORKSPACE} -type f -name "*.log" -mtime +7 -delete 2>/dev/null || true
+            '''
+        }
+    }
+}

+ 89 - 0
build-docker-optimized.ps1

@@ -0,0 +1,89 @@
+# ============================================
+# 快速 Docker 构建脚本 - PowerShell 版本
+# 使用优化版 Dockerfile
+# 构建前请先执行:.\init-dependencies.ps1
+# ============================================
+
+param(
+    [string]$Service = "gateway",
+    [string]$Tag = "latest",
+    [string]$Registry = "localhost:5000"
+)
+
+$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
+Set-Location $scriptDir
+
+$buildNumber = Get-Date -Format "yyyyMMddHHmmss"
+try {
+    $gitCommit = git rev-parse --short HEAD
+} catch {
+    $gitCommit = "unknown"
+}
+
+$dockerfile = ""
+$imageName = ""
+
+switch ($Service) {
+    "gateway" {
+        $dockerfile = "shop-recycle-gateway/Dockerfile.optimized"
+        $imageName = "$Registry/shop-recycle-gateway:$Tag"
+    }
+    "order" {
+        $dockerfile = "shop-recycle-order-service/Dockerfile.optimized"
+        $imageName = "$Registry/shop-recycle-order-service:$Tag"
+    }
+    "payment" {
+        $dockerfile = "shop-recycle-payment-service/Dockerfile.optimized"
+        $imageName = "$Registry/shop-recycle-payment-service:$Tag"
+    }
+    default {
+        Write-Host "未知服务: $Service" -ForegroundColor Red
+        Write-Host "用法: .\build-docker-optimized.ps1 [gateway|order|payment] [tag] [registry]" -ForegroundColor Yellow
+        exit 1
+    }
+}
+
+Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Green
+Write-Host "快速构建 - 使用优化版 Dockerfile" -ForegroundColor Green
+Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Green
+Write-Host "服务: $Service" -ForegroundColor Cyan
+Write-Host "TAG: $Tag" -ForegroundColor Cyan
+Write-Host "镜像: $imageName" -ForegroundColor Cyan
+Write-Host "Commit: $gitCommit" -ForegroundColor Cyan
+Write-Host ""
+
+# 检查 Dockerfile 是否存在
+if (-not (Test-Path $dockerfile)) {
+    Write-Host "❌ 错误: $dockerfile 不存在" -ForegroundColor Red
+    Write-Host ""
+    Write-Host "首次使用时,请先执行初始化脚本:" -ForegroundColor Yellow
+    Write-Host "  .\init-dependencies.ps1" -ForegroundColor Yellow
+    Write-Host "  .\build-docker-optimized.ps1 $Service $Tag" -ForegroundColor Yellow
+    exit 1
+}
+
+Write-Host ">>> 开始构建..." -ForegroundColor Yellow
+
+docker build `
+    --build-arg BUILD_NUMBER="$buildNumber" `
+    --build-arg GIT_COMMIT="$gitCommit" `
+    -t "$imageName" `
+    -f "$dockerfile" `
+    .
+
+if ($LASTEXITCODE -ne 0) {
+    Write-Host "❌ 构建失败!" -ForegroundColor Red
+    exit 1
+}
+
+Write-Host ""
+Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Green
+Write-Host "✅ 构建完成!" -ForegroundColor Green
+Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Green
+Write-Host ""
+
+Write-Host "镜像详细信息:"
+docker images | Select-Object -First 4
+
+Write-Host ""
+Write-Host "运行容器: docker run -d -p 8080:8080 $imageName" -ForegroundColor Cyan

+ 73 - 0
build-docker-optimized.sh

@@ -0,0 +1,73 @@
+#!/bin/bash
+
+# ============================================
+# 快速 Docker 构建脚本 - 使用优化版 Dockerfile
+# 构建前请先执行:bash init-dependencies.sh
+# ============================================
+
+set -e
+
+SERVICE=${1:-gateway}
+TAG=${2:-latest}
+REGISTRY=${3:-localhost:5000}
+
+BUILD_NUMBER=$(date +%Y%m%d%H%M%S)
+GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
+
+case $SERVICE in
+    gateway)
+        DOCKERFILE="shop-recycle-gateway/Dockerfile.optimized"
+        IMAGE_NAME="$REGISTRY/shop-recycle-gateway:$TAG"
+        ;;
+    order)
+        DOCKERFILE="shop-recycle-order-service/Dockerfile.optimized"
+        IMAGE_NAME="$REGISTRY/shop-recycle-order-service:$TAG"
+        ;;
+    payment)
+        DOCKERFILE="shop-recycle-payment-service/Dockerfile.optimized"
+        IMAGE_NAME="$REGISTRY/shop-recycle-payment-service:$TAG"
+        ;;
+    *)
+        echo "未知服务: $SERVICE"
+        echo "用法: $0 [gateway|order|payment] [tag] [registry]"
+        exit 1
+        ;;
+esac
+
+echo "════════════════════════════════════════════════════════════"
+echo "快速构建 - 使用优化版 Dockerfile"
+echo "════════════════════════════════════════════════════════════"
+echo "服务: $SERVICE"
+echo "TAG: $TAG"
+echo "镜像: $IMAGE_NAME"
+echo "Commit: $GIT_COMMIT"
+echo ""
+
+# 检查 Dockerfile 是否存在
+if [ ! -f "$DOCKERFILE" ]; then
+    echo "❌ 错误: $DOCKERFILE 不存在"
+    echo ""
+    echo "首次使用时,请先执行初始化脚本:"
+    echo "  bash init-dependencies.sh"
+    echo "  bash build-docker-optimized.sh $SERVICE $TAG"
+    exit 1
+fi
+
+echo ">>> 开始构建..."
+docker build \
+    --build-arg BUILD_NUMBER="$BUILD_NUMBER" \
+    --build-arg GIT_COMMIT="$GIT_COMMIT" \
+    -t "$IMAGE_NAME" \
+    -f "$DOCKERFILE" \
+    .
+
+echo ""
+echo "════════════════════════════════════════════════════════════"
+echo "✅ 构建完成!"
+echo "════════════════════════════════════════════════════════════"
+echo ""
+echo "镜像详细信息:"
+docker images | grep "$REGISTRY" | head -3
+
+echo ""
+echo "运行容器: docker run -d -p 8080:8080 $IMAGE_NAME"

+ 5 - 1
build-docker.sh

@@ -31,6 +31,7 @@ declare -A SERVICES=(
     [gateway]="gateway"
     [order]="order-service"
     [payment]="payment-service"
+    [web]="web"
 )
 
 # ============================================
@@ -42,7 +43,7 @@ print_usage() {
 使用方法: $0 [SERVICE] [TAG] [REGISTRY]
 
 参数:
-    SERVICE   - 服务名称 (gateway|order|payment) [默认: gateway]
+    SERVICE   - 服务名称 (gateway|order|payment|web) [默认: gateway]
     TAG       - 镜像标签 [默认: latest]
     REGISTRY  - 镜像源 (aliyun|netease|tsinghua|ustc) [默认: aliyun]
 
@@ -56,6 +57,9 @@ print_usage() {
     # 构建payment-service镜像,使用清华源
     $0 payment v1.0.0-001 tsinghua
 
+    # 构建web前端镜像
+    $0 web latest aliyun
+
     # 智能选择镜像源(自动检测最快源)
     $0 gateway latest auto
 EOF

+ 14 - 0
docker-compose.yml

@@ -43,6 +43,20 @@ services:
     networks:
       - app-network
 
+  # Web Frontend
+  web:
+    build:
+      context: .
+      dockerfile: shop-recycle-web/Dockerfile
+    ports:
+      - "80:80"
+    environment:
+      - TZ=Asia/Shanghai
+    depends_on:
+      - gateway
+    networks:
+      - app-network
+
 networks:
   app-network:
     driver: bridge

+ 41 - 0
init-dependencies.ps1

@@ -0,0 +1,41 @@
+# ============================================
+# 快速初始化脚本 - PowerShell 版本
+# 预安装依赖模块到本地仓库
+# ============================================
+
+Write-Host "════════════════════════════════════════" -ForegroundColor Green
+Write-Host "预安装 shop-recycle-common 模块" -ForegroundColor Green
+Write-Host "════════════════════════════════════════" -ForegroundColor Green
+Write-Host ""
+
+# 获取脚本所在目录
+$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
+Set-Location $scriptDir
+
+# 构建并安装 common 模块到本地 Maven 仓库
+Write-Host ">>> 构建 shop-recycle-common 模块..." -ForegroundColor Yellow
+mvn clean install -pl shop-recycle-common `
+    -DskipTests `
+    -B -q `
+    -Dmaven.test.skip=true
+
+if ($LASTEXITCODE -ne 0) {
+    Write-Host "❌ 构建失败!" -ForegroundColor Red
+    exit 1
+}
+
+Write-Host ""
+Write-Host "════════════════════════════════════════" -ForegroundColor Green
+Write-Host "✅ 完成!common 模块已安装到本地仓库" -ForegroundColor Green
+Write-Host "════════════════════════════════════════" -ForegroundColor Green
+Write-Host ""
+Write-Host "本地仓库位置: ~/.m2/repository/com/shop/recycle/shop-recycle-common/"
+Write-Host ""
+Write-Host "现在可以使用优化版 Dockerfile 进行快速构建:"
+Write-Host ""
+Write-Host "  # 使用缓存的优化版 Dockerfile" -ForegroundColor Cyan
+Write-Host "  docker build -f shop-recycle-gateway/Dockerfile.optimized -t gateway:latest ." -ForegroundColor Cyan
+Write-Host ""
+Write-Host "  # 或者使用快速构建脚本" -ForegroundColor Cyan
+Write-Host '  .\build-docker-optimized.ps1 gateway latest' -ForegroundColor Cyan
+Write-Host ""

+ 39 - 0
init-dependencies.sh

@@ -0,0 +1,39 @@
+#!/bin/bash
+
+# ============================================
+# 快速初始化脚本 - 预安装依赖模块到本地仓库
+# 这样 Docker 构建时可以大幅加速
+# ============================================
+
+set -e
+
+echo "════════════════════════════════════════"
+echo "预安装 shop-recycle-common 模块"
+echo "════════════════════════════════════════"
+echo ""
+
+# 在项目根目录执行
+cd "$(dirname "$0")"
+
+# 构建并安装 common 模块到本地 Maven 仓库
+echo ">>> 构建 shop-recycle-common 模块..."
+mvn clean install -pl shop-recycle-common \
+    -DskipTests \
+    -B -q \
+    -Dmaven.test.skip=true
+
+echo ""
+echo "════════════════════════════════════════"
+echo "✅ 完成!common 模块已安装到本地仓库"
+echo "════════════════════════════════════════"
+echo ""
+echo "本地仓库位置: ~/.m2/repository/com/shop/recycle/shop-recycle-common/"
+echo ""
+echo "现在可以使用优化版 Dockerfile 进行快速构建:"
+echo ""
+echo "  # 使用缓存的优化版 Dockerfile"
+echo "  docker build -f shop-recycle-gateway/Dockerfile.optimized -t gateway:latest ."
+echo ""
+echo "  # 或者使用原始版本(首次构建会较慢)"
+echo "  docker build -f shop-recycle-gateway/Dockerfile -t gateway:latest ."
+echo ""

Plik diff jest za duży
+ 1788 - 0
k8s-rendered.yaml


+ 12 - 0
k8s/helm/shop-recycle/templates/_helpers.tpl

@@ -70,3 +70,15 @@ Return image reference
 {{- $tag := .Values.image.tag | default .Chart.AppVersion }}
 {{- printf "%s/%s/%s:%s" $registry $namespace $name $tag }}
 {{- end }}
+
+{{/*
+Generate Docker registry config (.dockercfg) for imagePullSecret
+*/}}
+{{- define "shop-recycle.registrySecret" -}}
+{{- $username := .Values.global.registry.username }}
+{{- $password := .Values.global.registry.password }}
+{{- $email := .Values.global.registry.email }}
+{{- $registry := .Values.image.registry }}
+{{- $auth := printf "%s:%s" $username $password | b64enc }}
+{{- printf "{%q:{%q:%q,%q:%q,%q:%q,%q:%q}}" $registry "username" $username "password" $password "email" $email "auth" $auth }}
+{{- end }}

+ 12 - 4
k8s/helm/shop-recycle/templates/deployment-gateway.yaml

@@ -26,10 +26,14 @@ spec:
       {{- if .Values.rbac.create }}
       serviceAccountName: {{ include "shop-recycle.serviceAccountName" . }}
       {{- end }}
-      
-      {{- with .Values.affinity }}
-      affinity:
-        {{- toYaml . | nindent 8 }}
+      {{- if or .Values.global.registry.createSecret .Values.global.imagePullSecrets }}
+      imagePullSecrets:
+      {{- if .Values.global.registry.createSecret }}
+        - name: {{ include "shop-recycle.fullname" . }}-registry-secret
+      {{- end }}
+      {{- with .Values.global.imagePullSecrets }}
+{{ toYaml . | indent 8 }}
+      {{- end }}
       {{- end }}
       
       {{- with .Values.tolerations }}
@@ -41,6 +45,10 @@ spec:
         - name: gateway
           image: "{{ .Values.image.registry }}/{{ .Values.image.namespace }}/{{ .Values.gateway.image.name }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
           imagePullPolicy: {{ .Values.image.pullPolicy }}
+          command:
+            - sh
+            - -c
+            - java ${JAVA_OPTS} -jar /app/gateway.jar
           
           ports:
             - name: http

+ 12 - 4
k8s/helm/shop-recycle/templates/deployment-order-service.yaml

@@ -26,10 +26,14 @@ spec:
       {{- if .Values.rbac.create }}
       serviceAccountName: {{ include "shop-recycle.serviceAccountName" . }}
       {{- end }}
-      
-      {{- with .Values.affinity }}
-      affinity:
-        {{- toYaml . | nindent 8 }}
+      {{- if or .Values.global.registry.createSecret .Values.global.imagePullSecrets }}
+      imagePullSecrets:
+      {{- if .Values.global.registry.createSecret }}
+        - name: {{ include "shop-recycle.fullname" . }}-registry-secret
+      {{- end }}
+      {{- with .Values.global.imagePullSecrets }}
+{{ toYaml . | indent 8 }}
+      {{- end }}
       {{- end }}
       
       {{- with .Values.tolerations }}
@@ -41,6 +45,10 @@ spec:
         - name: order-service
           image: "{{ .Values.image.registry }}/{{ .Values.image.namespace }}/{{ .Values.orderService.image.name }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
           imagePullPolicy: {{ .Values.image.pullPolicy }}
+          command:
+            - sh
+            - -c
+            - java ${JAVA_OPTS} -jar /app/order-service.jar
           
           ports:
             - name: http

+ 12 - 4
k8s/helm/shop-recycle/templates/deployment-payment-service.yaml

@@ -26,10 +26,14 @@ spec:
       {{- if .Values.rbac.create }}
       serviceAccountName: {{ include "shop-recycle.serviceAccountName" . }}
       {{- end }}
-      
-      {{- with .Values.affinity }}
-      affinity:
-        {{- toYaml . | nindent 8 }}
+      {{- if or .Values.global.registry.createSecret .Values.global.imagePullSecrets }}
+      imagePullSecrets:
+      {{- if .Values.global.registry.createSecret }}
+        - name: {{ include "shop-recycle.fullname" . }}-registry-secret
+      {{- end }}
+      {{- with .Values.global.imagePullSecrets }}
+{{ toYaml . | indent 8 }}
+      {{- end }}
       {{- end }}
       
       {{- with .Values.tolerations }}
@@ -41,6 +45,10 @@ spec:
         - name: payment-service
           image: "{{ .Values.image.registry }}/{{ .Values.image.namespace }}/{{ .Values.paymentService.image.name }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
           imagePullPolicy: {{ .Values.image.pullPolicy }}
+          command:
+            - sh
+            - -c
+            - java ${JAVA_OPTS} -jar /app/payment-service.jar
           
           ports:
             - name: http

+ 68 - 0
k8s/helm/shop-recycle/templates/deployment-web.yaml

@@ -0,0 +1,68 @@
+{{- if .Values.webFrontend.enabled }}
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ include "shop-recycle.fullname" . }}-web
+  labels:
+    app: {{ include "shop-recycle.fullname" . }}-web
+    {{- include "shop-recycle.labels" . | nindent 4 }}
+  namespace: {{ .Release.Namespace }}
+spec:
+  replicas: {{ .Values.webFrontend.replicaCount }}
+  selector:
+    matchLabels:
+      app: {{ include "shop-recycle.fullname" . }}-web
+      {{- include "shop-recycle.selectorLabels" . | nindent 6 }}
+  template:
+    metadata:
+      labels:
+        app: {{ include "shop-recycle.fullname" . }}-web
+        {{- include "shop-recycle.selectorLabels" . | nindent 8 }}
+    spec:
+      {{- if .Values.rbac.create }}
+      serviceAccountName: {{ include "shop-recycle.serviceAccountName" . }}
+      {{- end }}
+      {{- if or .Values.global.registry.createSecret .Values.global.imagePullSecrets }}
+      imagePullSecrets:
+      {{- if .Values.global.registry.createSecret }}
+        - name: {{ include "shop-recycle.fullname" . }}-registry-secret
+      {{- end }}
+      {{- with .Values.global.imagePullSecrets }}
+{{ toYaml . | indent 8 }}
+      {{- end }}
+      {{- end }}
+      
+      {{- with .Values.tolerations }}
+      tolerations:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+
+      containers:
+        - name: web
+          image: "{{ .Values.image.registry }}/{{ .Values.image.namespace }}/{{ .Values.webFrontend.image.name }}:{{ .Values.webFrontend.image.tag | default .Values.image.tag | default .Chart.AppVersion }}"
+          imagePullPolicy: {{ .Values.image.pullPolicy }}
+
+          ports:
+            - name: http
+              containerPort: {{ .Values.webFrontend.containerPort }}
+              protocol: {{ .Values.webFrontend.protocol }}
+
+          env:
+            {{- range $key, $value := .Values.webFrontend.env }}
+            - name: {{ $key }}
+              value: "{{ $value }}"
+            {{- end }}
+
+          {{- if .Values.webFrontend.livenessProbe }}
+          livenessProbe:
+            {{- toYaml .Values.webFrontend.livenessProbe | nindent 12 }}
+          {{- end }}
+
+          {{- if .Values.webFrontend.readinessProbe }}
+          readinessProbe:
+            {{- toYaml .Values.webFrontend.readinessProbe | nindent 12 }}
+          {{- end }}
+
+          resources:
+            {{- toYaml .Values.webFrontend.resources | nindent 12 }}
+{{- end }}

+ 39 - 0
k8s/helm/shop-recycle/templates/ingress-web.yaml

@@ -0,0 +1,39 @@
+{{- if .Values.webFrontend.ingress.enabled }}
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  name: {{ include "shop-recycle.fullname" . }}-web
+  labels:
+    app: {{ include "shop-recycle.fullname" . }}-web
+    {{- include "shop-recycle.labels" . | nindent 4 }}
+  namespace: {{ .Release.Namespace }}
+  {{- with .Values.webFrontend.ingress.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
+spec:
+  {{- if .Values.webFrontend.ingress.className }}
+  ingressClassName: {{ .Values.webFrontend.ingress.className }}
+  {{- end }}
+  {{- with .Values.webFrontend.ingress.tls }}
+  tls:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
+  rules:
+    {{- $root := . }}
+    {{- $svcPort := .Values.webFrontend.service.port }}
+    {{- range .Values.webFrontend.ingress.hosts }}
+    - host: {{ .host | quote }}
+      http:
+        paths:
+          {{- range .paths }}
+          - path: {{ .path }}
+            pathType: {{ .pathType }}
+            backend:
+              service:
+                name: {{ include "shop-recycle.fullname" $root }}-web
+                port:
+                  number: {{ $svcPort }}
+          {{- end }}
+    {{- end }}
+{{- end }}

+ 12 - 0
k8s/helm/shop-recycle/templates/secret-registry.yaml

@@ -0,0 +1,12 @@
+{{- if .Values.global.registry.createSecret }}
+apiVersion: v1
+kind: Secret
+metadata:
+  name: {{ include "shop-recycle.fullname" . }}-registry-secret
+  labels:
+    {{- include "shop-recycle.labels" . | nindent 4 }}
+  namespace: {{ .Release.Namespace }}
+type: kubernetes.io/dockercfg
+data:
+  .dockercfg: {{ include "shop-recycle.registrySecret" . | b64enc }}
+{{- end }}

+ 25 - 0
k8s/helm/shop-recycle/templates/service.yaml

@@ -23,6 +23,31 @@ spec:
     {{- include "shop-recycle.selectorLabels" . | nindent 4 }}
 {{- end }}
 ---
+{{- if .Values.webFrontend.enabled }}
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{ include "shop-recycle.fullname" . }}-web
+  labels:
+    app: {{ include "shop-recycle.fullname" . }}-web
+    {{- include "shop-recycle.labels" . | nindent 4 }}
+  namespace: {{ .Release.Namespace }}
+  {{- with .Values.webFrontend.service.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
+spec:
+  type: {{ .Values.webFrontend.service.type }}
+  ports:
+    - port: {{ .Values.webFrontend.service.port }}
+      targetPort: {{ .Values.webFrontend.service.targetPort }}
+      protocol: {{ .Values.webFrontend.protocol }}
+      name: http
+  selector:
+    app: {{ include "shop-recycle.fullname" . }}-web
+    {{- include "shop-recycle.selectorLabels" . | nindent 4 }}
+{{- end }}
+---
 {{- if .Values.orderService.enabled }}
 apiVersion: v1
 kind: Service

+ 88 - 22
k8s/helm/shop-recycle/values.yaml

@@ -5,17 +5,26 @@
 
 # 全局配置
 global:
+  # 镜像仓库认证(若启用,Helm 自动生成 imagePullSecret)
+  registry:
+    createSecret: true  # 设为 false 则不创建 secret,使用已有的
+    username: "admin"        # harbor.stardance 用户名
+    password: "Harbor12345"        # harbor.stardance 密码
+    email: "ops@example.com"
+  
+  # 若 registry.createSecret=false,则指定已有的 secret 名称
   imagePullSecrets: []
-  # - name: aliyun-registry
+  # - name: reg-cred
   
 # 副本数
-replicaCount: 2
+replicaCount: 1
 
 # 镜像配置
 image:
-  registry: docker.io
-  namespace: shop-recycle
+  registry: harbor.stardance
+  namespace: shoprecycle
   pullPolicy: IfNotPresent
+  tag: "3.0.0"
   # tag由Jenkins Pipeline动态注入(--set image.tag=xxx)
 
 # 环境名称(由Pipeline动态注入)
@@ -26,11 +35,11 @@ environment: dev
 # ==========================================
 gateway:
   enabled: true
-  replicaCount: 2
+  replicaCount: 1
   
   image:
     name: gateway
-    # tag由全局image.tag继承
+    tag: "3.0.0"
   
   port: 8080
   containerPort: 8080
@@ -83,14 +92,14 @@ gateway:
     annotations:
       cert-manager.io/cluster-issuer: "letsencrypt-prod"
     hosts:
-      - host: "gateway.example.com"
+      - host: "dev.jxfxtd.com"
         paths:
           - path: /
             pathType: Prefix
     tls:
       - secretName: gateway-tls
         hosts:
-          - "gateway.example.com"
+          - "dev.jxfxtd.com"
   
   # 环境变量
   env:
@@ -131,10 +140,11 @@ gateway:
 # ==========================================
 orderService:
   enabled: true
-  replicaCount: 2
+  replicaCount: 1
   
   image:
     name: order-service
+    tag: "3.0.0"
   
   port: 8081
   containerPort: 8081
@@ -184,10 +194,11 @@ orderService:
 # ==========================================
 paymentService:
   enabled: true
-  replicaCount: 2
+  replicaCount: 1
   
   image:
     name: payment-service
+    tag: "3.0.0"
   
   port: 8082
   containerPort: 8082
@@ -233,6 +244,72 @@ paymentService:
     LOGGING_LEVEL_ROOT: "INFO"
 
 # ==========================================
+# Web Frontend (前端应用)
+# ==========================================
+webFrontend:
+  enabled: true
+  replicaCount: 1
+  
+  image:
+    name: web
+    tag: "3.0.0"
+  
+  port: 80
+  containerPort: 80
+  protocol: TCP
+  
+  resources:
+    requests:
+      memory: "128Mi"
+      cpu: "100m"
+    limits:
+      memory: "256Mi"
+      cpu: "500m"
+  
+  livenessProbe:
+    httpGet:
+      path: /health
+      port: 80
+    initialDelaySeconds: 10
+    periodSeconds: 10
+    timeoutSeconds: 5
+    failureThreshold: 3
+  
+  readinessProbe:
+    httpGet:
+      path: /
+      port: 80
+    initialDelaySeconds: 5
+    periodSeconds: 5
+    timeoutSeconds: 3
+    failureThreshold: 3
+  
+  service:
+    type: ClusterIP
+    port: 80
+    targetPort: 80
+    annotations: {}
+  
+  ingress:
+    enabled: true
+    className: "nginx"
+    annotations:
+      cert-manager.io/cluster-issuer: "letsencrypt-prod"
+    hosts:
+      - host: "dev.jxfxtd.com"
+        paths:
+          - path: /
+            pathType: Prefix
+    tls:
+      - secretName: web-tls
+        hosts:
+          - "dev.jxfxtd.com"
+  
+  env:
+    VUE_APP_API_BASE: "http://shop-recycle-gateway:8080"
+    LOGGING_LEVEL: "info"
+
+# ==========================================
 # 网络策略
 # ==========================================
 networkPolicy:
@@ -258,18 +335,7 @@ rbac:
 # ==========================================
 # 节点亲和性与污点容限
 # ==========================================
-affinity:
-  podAntiAffinity:
-    preferredDuringSchedulingIgnoredDuringExecution:
-      - weight: 100
-        podAffinityTerm:
-          labelSelector:
-            matchExpressions:
-              - key: app
-                operator: In
-                values:
-                  - shop-recycle-gateway
-          topologyKey: kubernetes.io/hostname
+affinity: {}
 
 tolerations: []
 

+ 15 - 8
shop-recycle-gateway/Dockerfile

@@ -1,6 +1,6 @@
 # ============================================
-# Build stage with incremental cache support
-# Alpha最小化镜像 + 官方镜像源
+# Build stage with optimized cache strategy
+# 优化缓存:分离 POM 和源代码复制
 # ============================================
 FROM maven:3.8-openjdk-8 AS builder
 
@@ -12,20 +12,27 @@ ENV MAVEN_OPTS="${MAVEN_OPTS}"
 
 WORKDIR /build
 
-# Copy parent pom first (if exists in multi-module project)
+# 第一层:复制 POM 和 common 完整源代码(缓存关键)
 COPY pom.xml .
 COPY shop-recycle-common/ shop-recycle-common/
 COPY shop-recycle-gateway/pom.xml shop-recycle-gateway/
 COPY shop-recycle-order-service/pom.xml shop-recycle-order-service/
 COPY shop-recycle-payment-service/pom.xml shop-recycle-payment-service/
 
-# Download dependencies (cached layer - only changes if pom.xml changes)
-RUN mvn dependency:go-offline -B -q
+# 第二层:构建 common 模块(完整源代码可用)
+RUN mvn install -pl shop-recycle-common \
+    -DskipTests -B -q \
+    -Dmaven.test.skip=true
 
-# Copy sources
-COPY shop-recycle-gateway/src shop-recycle-gateway/src
+# 第三层:仅下载依赖(POM 未变时可复用此层)
+RUN mvn dependency:go-offline -B -q \
+    -pl shop-recycle-gateway \
+    -am
+
+# 第四层:复制源代码(变化频繁,但前面的缓存都是热的)
+COPY shop-recycle-gateway/ shop-recycle-gateway/
 
-# Build with incremental compilation (no clean!)
+# 第五层:编译(无 clean 保留缓存)
 RUN mvn package -DskipTests -B -q \
     -pl shop-recycle-gateway \
     -am \

+ 55 - 0
shop-recycle-gateway/Dockerfile.optimized

@@ -0,0 +1,55 @@
+# ============================================
+# Build stage - 精简版(假设common已预先安装到本地仓库)
+# ============================================
+FROM maven:3.8-openjdk-8 AS builder
+
+ARG BUILD_NUMBER=local
+ARG GIT_COMMIT=unknown
+ARG MAVEN_OPTS=-Dmaven.wagon.http.ssl.insecure=true
+
+ENV MAVEN_OPTS="${MAVEN_OPTS}"
+
+WORKDIR /build
+
+# 只复制 pom.xml 和源代码(common 从本地仓库获取)
+COPY pom.xml .
+COPY shop-recycle-common/pom.xml ./shop-recycle-common/pom.xml
+COPY shop-recycle-gateway/ ./shop-recycle-gateway/
+COPY shop-recycle-order-service/pom.xml ./shop-recycle-order-service/
+COPY shop-recycle-payment-service/pom.xml ./shop-recycle-payment-service/
+
+# 仅下载依赖(common 会从本地仓库的缓存获取)
+RUN mvn dependency:go-offline -B -q \
+    -pl shop-recycle-gateway \
+    -am
+
+# 构建服务(跳过其他模块,只构建 gateway)
+RUN mvn package -DskipTests -B -q \
+    -pl shop-recycle-gateway \
+    -Dmaven.test.skip=true \
+    -Ddocker.build.number=${BUILD_NUMBER} \
+    -Dgit.commit=${GIT_COMMIT}
+
+# ============================================
+# Runtime stage
+# ============================================
+FROM openjdk:8-jre-alpine
+
+LABEL maintainer="shop-recycle"
+LABEL service="shop-recycle-gateway"
+LABEL build.number=${BUILD_NUMBER}
+LABEL git.commit=${GIT_COMMIT}
+
+ENV TZ=Asia/Shanghai
+ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
+
+WORKDIR /app
+
+COPY --from=builder /build/shop-recycle-gateway/target/*.jar gateway.jar
+
+EXPOSE 8080
+
+HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
+    CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
+
+ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar gateway.jar"]

+ 20 - 0
shop-recycle-gateway/pom.xml

@@ -35,4 +35,24 @@
             <artifactId>spring-boot-starter-actuator</artifactId>
         </dependency>
     </dependencies>
+
+    <build>
+        <finalName>gateway</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>com.shop.recycle.gateway.GatewayApplication</mainClass>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
 </project>

+ 15 - 8
shop-recycle-order-service/Dockerfile

@@ -1,6 +1,6 @@
 # ============================================
-# Build stage with incremental cache support
-# Alpine最小化镜像 + 官方镜像源
+# Build stage with optimized cache strategy
+# 优化缓存:分离 POM 和源代码复制
 # ============================================
 FROM maven:3.8-openjdk-8 AS builder
 
@@ -12,20 +12,27 @@ ENV MAVEN_OPTS="${MAVEN_OPTS}"
 
 WORKDIR /build
 
-# Copy parent pom first (if exists in multi-module project)
+# 第一层:复制 POM 和 common 完整源代码(缓存关键)
 COPY pom.xml .
 COPY shop-recycle-common/ shop-recycle-common/
 COPY shop-recycle-gateway/pom.xml shop-recycle-gateway/
 COPY shop-recycle-order-service/pom.xml shop-recycle-order-service/
 COPY shop-recycle-payment-service/pom.xml shop-recycle-payment-service/
 
-# Download dependencies (cached layer - only changes if pom.xml changes)
-RUN mvn dependency:go-offline -B -q
+# 第二层:构建 common 模块(完整源代码可用)
+RUN mvn install -pl shop-recycle-common \
+    -DskipTests -B -q \
+    -Dmaven.test.skip=true
 
-# Copy sources
-COPY shop-recycle-order-service/src shop-recycle-order-service/src
+# 第三层:仅下载依赖(POM 未变时可复用此层)
+RUN mvn dependency:go-offline -B -q \
+    -pl shop-recycle-order-service \
+    -am
+
+# 第四层:复制源代码(变化频繁,但前面的缓存都是热的)
+COPY shop-recycle-order-service/ shop-recycle-order-service/
 
-# Build with incremental compilation (no clean!)
+# 第五层:编译(无 clean 保留缓存)
 RUN mvn package -DskipTests -B -q \
     -pl shop-recycle-order-service \
     -am \

+ 55 - 0
shop-recycle-order-service/Dockerfile.optimized

@@ -0,0 +1,55 @@
+# ============================================
+# Build stage - 精简版(假设common已预先安装到本地仓库)
+# ============================================
+FROM maven:3.8-openjdk-8 AS builder
+
+ARG BUILD_NUMBER=local
+ARG GIT_COMMIT=unknown
+ARG MAVEN_OPTS=-Dmaven.wagon.http.ssl.insecure=true
+
+ENV MAVEN_OPTS="${MAVEN_OPTS}"
+
+WORKDIR /build
+
+# 只复制 pom.xml 和源代码(common 从本地仓库获取)
+COPY pom.xml .
+COPY shop-recycle-common/pom.xml ./shop-recycle-common/pom.xml
+COPY shop-recycle-order-service/ ./shop-recycle-order-service/
+COPY shop-recycle-gateway/pom.xml ./shop-recycle-gateway/
+COPY shop-recycle-payment-service/pom.xml ./shop-recycle-payment-service/
+
+# 仅下载依赖(common 会从本地仓库的缓存获取)
+RUN mvn dependency:go-offline -B -q \
+    -pl shop-recycle-order-service \
+    -am
+
+# 构建服务(跳过其他模块,只构建 order-service)
+RUN mvn package -DskipTests -B -q \
+    -pl shop-recycle-order-service \
+    -Dmaven.test.skip=true \
+    -Ddocker.build.number=${BUILD_NUMBER} \
+    -Dgit.commit=${GIT_COMMIT}
+
+# ============================================
+# Runtime stage
+# ============================================
+FROM openjdk:8-jre-alpine
+
+LABEL maintainer="shop-recycle"
+LABEL service="shop-recycle-order-service"
+LABEL build.number=${BUILD_NUMBER}
+LABEL git.commit=${GIT_COMMIT}
+
+ENV TZ=Asia/Shanghai
+ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
+
+WORKDIR /app
+
+COPY --from=builder /build/shop-recycle-order-service/target/*.jar order-service.jar
+
+EXPOSE 8081
+
+HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
+    CMD wget --no-verbose --tries=1 --spider http://localhost:8081/health || exit 1
+
+ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar order-service.jar"]

+ 20 - 0
shop-recycle-order-service/pom.xml

@@ -40,4 +40,24 @@
             <artifactId>spring-boot-starter-actuator</artifactId>
         </dependency>
     </dependencies>
+
+    <build>
+        <finalName>order-service</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>com.shop.recycle.order.OrderServiceApplication</mainClass>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
 </project>

+ 15 - 8
shop-recycle-payment-service/Dockerfile

@@ -1,6 +1,6 @@
 # ============================================
-# Build stage with incremental cache support
-# Alpine最小化镜像 + 官方镜像源
+# Build stage with optimized cache strategy
+# 优化缓存:分离 POM 和源代码复制
 # ============================================
 FROM maven:3.8-openjdk-8 AS builder
 
@@ -12,20 +12,27 @@ ENV MAVEN_OPTS="${MAVEN_OPTS}"
 
 WORKDIR /build
 
-# Copy parent pom first (if exists in multi-module project)
+# 第一层:复制 POM 和 common 完整源代码(缓存关键)
 COPY pom.xml .
 COPY shop-recycle-common/ shop-recycle-common/
 COPY shop-recycle-gateway/pom.xml shop-recycle-gateway/
 COPY shop-recycle-order-service/pom.xml shop-recycle-order-service/
 COPY shop-recycle-payment-service/pom.xml shop-recycle-payment-service/
 
-# Download dependencies (cached layer - only changes if pom.xml changes)
-RUN mvn dependency:go-offline -B -q
+# 第二层:构建 common 模块(完整源代码可用)
+RUN mvn install -pl shop-recycle-common \
+    -DskipTests -B -q \
+    -Dmaven.test.skip=true
 
-# Copy sources
-COPY shop-recycle-payment-service/src shop-recycle-payment-service/src
+# 第三层:仅下载依赖(POM 未变时可复用此层)
+RUN mvn dependency:go-offline -B -q \
+    -pl shop-recycle-payment-service \
+    -am
+
+# 第四层:复制源代码(变化频繁,但前面的缓存都是热的)
+COPY shop-recycle-payment-service/ shop-recycle-payment-service/
 
-# Build with incremental compilation (no clean!)
+# 第五层:编译(无 clean 保留缓存)
 RUN mvn package -DskipTests -B -q \
     -pl shop-recycle-payment-service \
     -am \

+ 55 - 0
shop-recycle-payment-service/Dockerfile.optimized

@@ -0,0 +1,55 @@
+# ============================================
+# Build stage - 精简版(假设common已预先安装到本地仓库)
+# ============================================
+FROM maven:3.8-openjdk-8 AS builder
+
+ARG BUILD_NUMBER=local
+ARG GIT_COMMIT=unknown
+ARG MAVEN_OPTS=-Dmaven.wagon.http.ssl.insecure=true
+
+ENV MAVEN_OPTS="${MAVEN_OPTS}"
+
+WORKDIR /build
+
+# 只复制 pom.xml 和源代码(common 从本地仓库获取)
+COPY pom.xml .
+COPY shop-recycle-common/pom.xml ./shop-recycle-common/pom.xml
+COPY shop-recycle-payment-service/ ./shop-recycle-payment-service/
+COPY shop-recycle-gateway/pom.xml ./shop-recycle-gateway/
+COPY shop-recycle-order-service/pom.xml ./shop-recycle-order-service/
+
+# 仅下载依赖(common 会从本地仓库的缓存获取)
+RUN mvn dependency:go-offline -B -q \
+    -pl shop-recycle-payment-service \
+    -am
+
+# 构建服务(跳过其他模块,只构建 payment-service)
+RUN mvn package -DskipTests -B -q \
+    -pl shop-recycle-payment-service \
+    -Dmaven.test.skip=true \
+    -Ddocker.build.number=${BUILD_NUMBER} \
+    -Dgit.commit=${GIT_COMMIT}
+
+# ============================================
+# Runtime stage
+# ============================================
+FROM openjdk:8-jre-alpine
+
+LABEL maintainer="shop-recycle"
+LABEL service="shop-recycle-payment-service"
+LABEL build.number=${BUILD_NUMBER}
+LABEL git.commit=${GIT_COMMIT}
+
+ENV TZ=Asia/Shanghai
+ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
+
+WORKDIR /app
+
+COPY --from=builder /build/shop-recycle-payment-service/target/*.jar payment-service.jar
+
+EXPOSE 8082
+
+HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
+    CMD wget --no-verbose --tries=1 --spider http://localhost:8082/health || exit 1
+
+ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar payment-service.jar"]

+ 20 - 0
shop-recycle-payment-service/pom.xml

@@ -40,4 +40,24 @@
             <artifactId>spring-boot-starter-actuator</artifactId>
         </dependency>
     </dependencies>
+
+    <build>
+        <finalName>payment-service</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>com.shop.recycle.payment.PaymentServiceApplication</mainClass>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
 </project>

+ 57 - 0
shop-recycle-web/Dockerfile

@@ -0,0 +1,57 @@
+# ============================================
+# Build stage with incremental cache support
+# Node.js前端构建
+# ============================================
+FROM node:18-alpine AS builder
+
+ARG BUILD_NUMBER=local
+ARG GIT_COMMIT=unknown
+
+WORKDIR /build
+
+# Copy package files
+COPY shop-recycle-web/package.json .
+COPY shop-recycle-web/package-lock.json ./
+
+# Install dependencies (cached layer - only changes if package.json changes)
+RUN npm ci --only=production && \
+    npm ci
+
+# Copy source code
+COPY shop-recycle-web/src ./src
+COPY shop-recycle-web/public ./public
+COPY shop-recycle-web/vite.config.js ./
+COPY shop-recycle-web/index.html ./
+
+# Build application
+RUN npm run build
+
+# ============================================
+# Runtime stage - ultra-minimal image
+# 使用Nginx Alpine基础镜像提供静态资源服务
+# ============================================
+FROM nginx:1.25-alpine
+
+LABEL maintainer="shop-recycle"
+LABEL service="shop-recycle-web"
+LABEL build.number=${BUILD_NUMBER}
+LABEL git.commit=${GIT_COMMIT}
+
+ENV TZ=Asia/Shanghai
+
+# Remove default nginx config
+RUN rm -rf /etc/nginx/conf.d/default.conf
+
+# Copy nginx configuration
+COPY shop-recycle-web/nginx.conf /etc/nginx/conf.d/default.conf
+
+# Copy built static files from builder stage
+COPY --from=builder /build/dist /usr/share/nginx/html
+
+EXPOSE 80
+
+# Health check
+HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
+    CMD wget --no-verbose --tries=1 --spider http://localhost/health || exit 0
+
+ENTRYPOINT ["nginx", "-g", "daemon off;"]

+ 61 - 0
shop-recycle-web/Dockerfile.multiarch

@@ -0,0 +1,61 @@
+# ============================================
+# Build stage for Frontend with multi-registry support
+# Node.js + Nginx 多镜像源支持
+# ============================================
+
+# 使用方式:
+# docker build -t web:latest -f shop-recycle-web/Dockerfile.multiarch .                      # 默认使用官方镜像
+# docker build --build-arg NODE_REGISTRY=registry.aliyuncs.com -t web:latest -f shop-recycle-web/Dockerfile.multiarch .
+
+ARG NODE_REGISTRY=node
+ARG NGINX_REGISTRY=nginx
+
+# ====== Build Stage ======
+FROM ${NODE_REGISTRY}:18-alpine AS builder
+
+ARG BUILD_NUMBER=local
+ARG GIT_COMMIT=unknown
+
+WORKDIR /build
+
+# Copy package files
+COPY shop-recycle-web/package*.json ./
+
+# Install dependencies (cached layer - only changes if package.json changes)
+RUN npm ci
+
+# Copy source code
+COPY shop-recycle-web/src ./src
+COPY shop-recycle-web/public ./public
+COPY shop-recycle-web/vite.config.js ./
+COPY shop-recycle-web/index.html ./
+
+# Build application
+RUN npm run build
+
+# ====== Runtime Stage ======
+FROM ${NGINX_REGISTRY}:1.25-alpine
+
+LABEL maintainer="shop-recycle"
+LABEL service="shop-recycle-web"
+LABEL build.number=${BUILD_NUMBER}
+LABEL git.commit=${GIT_COMMIT}
+
+ENV TZ=Asia/Shanghai
+
+# Remove default nginx config
+RUN rm -rf /etc/nginx/conf.d/default.conf
+
+# Copy nginx configuration
+COPY shop-recycle-web/nginx.conf /etc/nginx/conf.d/default.conf
+
+# Copy built static files from builder stage
+COPY --from=builder /build/dist /usr/share/nginx/html
+
+EXPOSE 80
+
+# Health check
+HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
+    CMD wget --no-verbose --tries=1 --spider http://localhost/health || exit 0
+
+ENTRYPOINT ["nginx", "-g", "daemon off;"]

+ 68 - 0
shop-recycle-web/nginx.conf

@@ -0,0 +1,68 @@
+
+server {
+    listen 80;
+    server_name localhost;
+
+    # 根目录配置
+    root /usr/share/nginx/html;
+    index index.html;
+
+    # 禁用缓存,便于开发和调试
+    add_header Cache-Control "no-cache, no-store, must-revalidate";
+    add_header Pragma "no-cache";
+    add_header Expires "0";
+
+    # 健康检查端点
+    location /health {
+        access_log off;
+        return 200 "healthy\n";
+        add_header Content-Type text/plain;
+    }
+
+    # 静态资源配置(带缓存)
+    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
+        expires 1y;
+        add_header Cache-Control "public, immutable";
+        add_header Access-Control-Allow-Origin "*";
+    }
+
+    # 反向代理到API网关
+    location /api/ {
+        proxy_pass http://localhost:8080/api/;
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+        proxy_read_timeout 30s;
+        proxy_connect_timeout 10s;
+    }
+
+    # Vue Router SPA 配置 - 所有非API请求都返回 index.html
+    location / {
+        try_files $uri $uri/ /index.html;
+    }
+
+    # 禁用特定文件访问
+    location ~ /\. {
+        deny all;
+        access_log off;
+        log_not_found off;
+    }
+
+    location ~ ~$ {
+        deny all;
+        access_log off;
+        log_not_found off;
+    }
+
+    # 错误页面配置
+    error_page 404 /index.html;
+    error_page 500 502 503 504 /50x.html;
+    location = /50x.html {
+        root /usr/share/nginx/html;
+    }
+
+    # 日志配置
+    access_log /var/log/nginx/access.log;
+    error_log /var/log/nginx/error.log warn;
+}