| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- #!/usr/bin/env bash
- # 在线订餐管理系统后台 Docker Compose 启动脚本(仅后台容器,MySQL/Redis 使用现网)
- # 默认镜像: harbor.sxidc.com/ywzx/fenyangwenlv:v1.0.0
- #
- # 用法:
- # ./start.sh up # 本地编译 + 构建镜像 + 启动(输出精简)
- # ./start.sh up --pull # 从 Harbor 拉取镜像并启动
- # ./start.sh up -v # 显示完整编译/构建日志
- # ./start.sh pull # 仅拉取镜像
- # ./start.sh push # 本地打包、构建并推送到 Harbor(需 docker login harbor.sxidc.com)
- # ./start.sh build # 本地构建镜像(不打 tag 推送)
- # ./start.sh build-jar # 仅 Maven 打包
- # ./start.sh logs # 查看日志
- # ./start.sh down # 停止容器
- set -euo pipefail
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
- MAVEN_ROOT=""
- BUILD_CONTEXT=""
- DOCKER_DIR=""
- ENV_FILE=""
- COMPOSE_FILE=""
- DOCKERFILE_PATH=""
- RED='\033[0;31m'
- GREEN='\033[0;32m'
- YELLOW='\033[1;33m'
- NC='\033[0m'
- log_info() { echo -e "${GREEN}[INFO]${NC} $*"; }
- log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
- log_error() { echo -e "${RED}[ERROR]${NC} $*"; }
- require_cmd() {
- if ! command -v "$1" >/dev/null 2>&1; then
- log_error "未找到命令: $1"
- exit 1
- fi
- }
- load_env() {
- # shellcheck disable=SC1090
- set -a
- source "${ENV_FILE}" 2>/dev/null || true
- set +a
- }
- # 判断目录是否为 Maven 多模块根(含 web-starter 子模块)
- is_maven_project_root() {
- local dir="$1"
- [[ -f "${dir}/pom.xml" && -f "${dir}/web-starter/pom.xml" ]]
- }
- # 解析 docker 目录、compose 文件与 Maven 工程根(兼容脚本在 docker/ 或部署根目录两种布局)
- init_paths() {
- if [[ -f "${SCRIPT_DIR}/docker-compose.yml" ]]; then
- DOCKER_DIR="${SCRIPT_DIR}"
- elif [[ -f "${SCRIPT_DIR}/docker/docker-compose.yml" ]]; then
- DOCKER_DIR="${SCRIPT_DIR}/docker"
- else
- log_error "未找到 docker-compose.yml(请在 docker/ 目录或部署根目录执行 start.sh)"
- exit 1
- fi
- ENV_FILE="${DOCKER_DIR}/.env"
- COMPOSE_FILE="${DOCKER_DIR}/docker-compose.yml"
- if [[ -f "${DOCKER_DIR}/Dockerfile" ]]; then
- DOCKERFILE_PATH="${DOCKER_DIR}/Dockerfile"
- else
- DOCKERFILE_PATH="${DOCKER_DIR}/docker/Dockerfile"
- fi
- # 先读 .env,允许 PROJECT_ROOT 显式指定 Maven 根
- if [[ -f "${ENV_FILE}" ]]; then
- load_env
- fi
- local from_env="${PROJECT_ROOT:-}"
- MAVEN_ROOT=""
- if [[ -n "${from_env}" ]] && is_maven_project_root "${from_env}"; then
- MAVEN_ROOT="$(cd "${from_env}" && pwd)"
- else
- local candidate
- for candidate in \
- "${SCRIPT_DIR}" \
- "${SCRIPT_DIR}/.." \
- "${DOCKER_DIR}" \
- "${DOCKER_DIR}/.." \
- "${DOCKER_DIR}/../TravelWebSystem" \
- "${SCRIPT_DIR}/TravelWebSystem"; do
- if is_maven_project_root "${candidate}"; then
- MAVEN_ROOT="$(cd "${candidate}" && pwd)"
- break
- fi
- done
- fi
- BUILD_CONTEXT="${MAVEN_ROOT:-${DOCKER_DIR}}"
- export MAVEN_ROOT
- export BUILD_CONTEXT
- export PROJECT_ROOT="${BUILD_CONTEXT}"
- export DOCKERFILE_PATH
- }
- compose() {
- if docker compose version >/dev/null 2>&1; then
- docker compose --env-file "${ENV_FILE}" -f "${COMPOSE_FILE}" "$@"
- elif command -v docker-compose >/dev/null 2>&1; then
- docker-compose --env-file "${ENV_FILE}" -f "${COMPOSE_FILE}" "$@"
- else
- log_error "未找到 docker compose / docker-compose,请安装 Docker Compose v2"
- exit 1
- fi
- }
- default_jar_path() {
- echo "${APP_JAR:-web-starter/target/web-starter-1.0-SNAPSHOT.jar}"
- }
- resolved_jar() {
- local jar
- jar="$(default_jar_path)"
- if [[ "${jar}" = /* ]]; then
- echo "${jar}"
- else
- echo "${BUILD_CONTEXT}/${jar}"
- fi
- }
- ensure_env() {
- if [[ ! -f "${ENV_FILE}" ]]; then
- if [[ -f "${SCRIPT_DIR}/.env.example" ]]; then
- cp "${SCRIPT_DIR}/.env.example" "${ENV_FILE}"
- log_warn "已生成 ${ENV_FILE},请填写现网 MYSQL_* / REDIS_* 后再启动"
- else
- log_error "缺少 ${ENV_FILE} 与 .env.example"
- exit 1
- fi
- fi
- load_env
- local missing=0
- for key in MYSQL_HOST MYSQL_USER MYSQL_PASSWORD REDIS_HOST; do
- if ! grep -q "^${key}=." "${ENV_FILE}" 2>/dev/null; then
- log_error ".env 缺少或未填写: ${key}"
- missing=1
- fi
- local val
- val="$(grep "^${key}=" "${ENV_FILE}" | cut -d= -f2- | tr -d '\r')"
- if [[ -z "${val}" || "${val}" == "请填写现网密码" ]]; then
- log_error ".env 中 ${key} 仍为占位值,请改成现网配置"
- missing=1
- fi
- done
- if [[ "${missing}" -eq 1 ]]; then
- exit 1
- fi
- mkdir -p "${DOCKER_DIR}/config"
- }
- ensure_docker() {
- require_cmd docker
- if ! docker info >/dev/null 2>&1; then
- log_error "Docker 未运行,请先启动 Docker Desktop / dockerd"
- exit 1
- fi
- }
- image_name() {
- load_env
- echo "${DOCKER_IMAGE:-harbor.sxidc.com/ywzx/fenyangwenlv:v1.0.0}"
- }
- BUILD_LOG=""
- COMPOSE_LOG=""
- is_verbose() {
- [[ "${VERBOSE:-0}" -eq 1 ]]
- }
- ensure_jar() {
- local jar
- jar="$(resolved_jar)"
- if [[ -f "${jar}" && "${SKIP_MAVEN_BUILD:-0}" == "1" ]]; then
- log_info "已存在 JAR,跳过 Maven 编译: ${jar}"
- return 0
- fi
- if [[ -z "${MAVEN_ROOT}" ]]; then
- if [[ -f "${jar}" ]]; then
- log_warn "未找到 Maven 工程根目录,使用已有 JAR: ${jar}"
- return 0
- fi
- log_error "未找到 Maven 工程(缺少 pom.xml 与 web-starter 模块)"
- log_error "请任选其一:"
- log_error " 1) 在 .env 设置 PROJECT_ROOT=完整源码目录(含 pom.xml、web-starter/)"
- log_error " 2) 放置 JAR 到 ${jar} 并设置 SKIP_MAVEN_BUILD=1"
- log_error " 3) 使用 Harbor 镜像: ./start.sh up --pull"
- exit 1
- fi
- require_cmd mvn
- log_info "Maven 工程根: ${MAVEN_ROOT}"
- if is_verbose; then
- log_info "Maven 打包 web-starter(跳过测试)..."
- (cd "${MAVEN_ROOT}" && mvn -B -f pom.xml -pl web-starter -am package -DskipTests)
- else
- log_info "正在编译..."
- : > "${BUILD_LOG}"
- if ! (cd "${MAVEN_ROOT}" && mvn -B -q -f pom.xml -pl web-starter -am package -DskipTests >>"${BUILD_LOG}" 2>&1); then
- log_error "编译失败,日志: ${BUILD_LOG}"
- tail -n 50 "${BUILD_LOG}" >&2
- exit 1
- fi
- fi
- if [[ ! -f "${jar}" ]]; then
- log_error "未找到构建产物: ${jar}"
- exit 1
- fi
- }
- compose_up_build() {
- if is_verbose; then
- log_info "构建镜像并启动容器..."
- compose up -d --build
- return
- fi
- log_info "正在构建镜像并启动..."
- : > "${COMPOSE_LOG}"
- if ! compose up -d --build --quiet-pull >>"${COMPOSE_LOG}" 2>&1; then
- log_error "构建或启动失败,日志: ${COMPOSE_LOG}"
- tail -n 50 "${COMPOSE_LOG}" >&2
- exit 1
- fi
- }
- compose_build() {
- if is_verbose; then
- compose build backend
- return
- fi
- : > "${COMPOSE_LOG}"
- if ! compose build backend >>"${COMPOSE_LOG}" 2>&1; then
- log_error "镜像构建失败,日志: ${COMPOSE_LOG}"
- tail -n 50 "${COMPOSE_LOG}" >&2
- exit 1
- fi
- }
- print_endpoints() {
- log_info "镜像: $(image_name)"
- log_info "后台 API: http://localhost:${SERVER_PORT:-8086}"
- log_info "Swagger: http://localhost:${SERVER_PORT:-8086}/swagger-ui.html"
- log_info "MySQL: ${MYSQL_HOST}:${MYSQL_PORT:-3306}/${MYSQL_DATABASE:-xinghc_travel}"
- log_info "Redis: ${REDIS_HOST}:${REDIS_PORT:-6379} db=${REDIS_DB:-1}"
- log_info "查看日志: ${SCRIPT_DIR}/start.sh logs"
- }
- cmd_pull() {
- ensure_docker
- ensure_env
- log_info "拉取镜像 $(image_name) ..."
- compose pull backend
- }
- cmd_up() {
- local do_pull=0
- local do_no_build=0
- VERBOSE=0
- for arg in "$@"; do
- case "${arg}" in
- --pull) do_pull=1 ;;
- --no-build) do_no_build=1 ;;
- -v|--verbose) VERBOSE=1 ;;
- esac
- done
- ensure_docker
- ensure_env
- if [[ "${do_pull}" -eq 1 ]]; then
- log_info "拉取镜像并启动: $(image_name)"
- compose pull backend
- compose up -d
- elif [[ "${do_no_build}" -eq 1 ]]; then
- log_info "启动已有镜像: $(image_name)"
- compose up -d
- else
- log_info "构建并启动后台: $(image_name)"
- ensure_jar
- compose_up_build
- log_info "启动完成"
- fi
- compose ps
- print_endpoints
- }
- cmd_build() {
- ensure_docker
- ensure_env
- VERBOSE="${VERBOSE:-0}"
- if [[ ! -f "$(resolved_jar)" ]]; then
- ensure_jar
- fi
- log_info "构建镜像: $(image_name)"
- compose_build
- }
- cmd_push() {
- ensure_docker
- ensure_env
- VERBOSE="${VERBOSE:-0}"
- ensure_jar
- local img
- img="$(image_name)"
- log_info "构建并推送: ${img}"
- compose_build
- docker push "${img}"
- log_info "推送完成: ${img}"
- }
- cmd_logs() {
- ensure_docker
- ensure_env
- compose logs -f --tail=200 backend
- }
- cmd_down() {
- ensure_docker
- ensure_env
- if [[ "${1:-}" == "-v" ]]; then
- log_warn "将删除 backend 数据卷(上传文件、日志等)"
- compose down -v
- else
- compose down
- fi
- }
- cmd_ps() {
- ensure_docker
- ensure_env
- compose ps -a
- }
- cmd_restart() {
- ensure_docker
- ensure_env
- compose restart backend
- }
- usage() {
- cat <<EOF
- 用法: $(basename "$0") <command>
- 命令:
- up [--pull] [--no-build] [-v] 构建并启动(--pull 用 Harbor;--no-build 不编译/不构建镜像)
- pull 仅拉取 Harbor 镜像
- push 本地打包、构建并 push 到 Harbor(需 docker login)
- build 仅本地构建镜像
- build-jar 仅 Maven 打包
- logs 跟踪 backend 日志
- ps 查看容器状态
- restart 重启 backend
- down 停止容器(保留数据卷)
- down -v 停止并删除数据卷
- 环境变量(.env):
- DOCKER_IMAGE 默认 harbor.sxidc.com/ywzx/fenyangwenlv:v1.0.0
- PROJECT_ROOT Maven 工程根(含 pom.xml、web-starter/);未设置则自动探测
- SKIP_MAVEN_BUILD=1 跳过 mvn,使用已有 JAR
- SPRING_ARGS 传给 java -jar 的 Spring 参数(如 --spring.config.location=...)
- HOST_CONFIG_DIR 宿主机配置目录,挂载为 /serverdata/config(默认 ./config)
- 外部配置: 复制 config/application.yaml.example 为 config/application.yaml 后修改
- EOF
- }
- main() {
- init_paths
- BUILD_LOG="${DOCKER_DIR}/.build.log"
- COMPOSE_LOG="${DOCKER_DIR}/.compose.log"
- local cmd="${1:-up}"
- shift || true
- case "${cmd}" in
- up) cmd_up "$@" ;;
- pull) cmd_pull ;;
- push) cmd_push ;;
- build) cmd_build ;;
- build-jar) VERBOSE=1; ensure_jar ;;
- logs) cmd_logs ;;
- down) cmd_down "$@" ;;
- ps) cmd_ps ;;
- restart) cmd_restart ;;
- -h|--help|help) usage ;;
- *)
- log_error "未知命令: ${cmd}"
- usage
- exit 1
- ;;
- esac
- }
- main "$@"
|