| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- #!/usr/bin/env bash
- set -euo pipefail
- project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
- frontend_pid=""
- backend_pid=""
- cleanup_done=0
- run_for_seconds="${ZBT_DEV_RUN_SECONDS:-0}"
- die() {
- echo "错误:$*" >&2
- exit 1
- }
- require_command() {
- local command_name="$1"
- command -v "$command_name" >/dev/null 2>&1 || \
- die "未找到 $command_name,请先安装并确保它位于 PATH 中。"
- }
- assert_port_available() {
- local port="$1"
- local application_name="$2"
- local listeners
- listeners="$(lsof -nP -iTCP:"$port" -sTCP:LISTEN 2>/dev/null || true)"
- if [[ -n "$listeners" ]]; then
- echo "$listeners" >&2
- die "$application_name 端口 $port 已被占用,请先结束上方进程后重试。"
- fi
- }
- process_group_is_alive() {
- local group_id="$1"
- [[ -n "$group_id" ]] && kill -0 -- "-$group_id" 2>/dev/null
- }
- stop_process_group() {
- local group_id="$1"
- if process_group_is_alive "$group_id"; then
- kill -TERM -- "-$group_id" 2>/dev/null || true
- fi
- }
- cleanup() {
- local exit_status=$?
- local deadline
- if (( cleanup_done )); then
- return "$exit_status"
- fi
- cleanup_done=1
- trap - INT TERM HUP
- if [[ -n "$frontend_pid" || -n "$backend_pid" ]]; then
- echo
- echo "正在停止智保通开发服务..."
- fi
- stop_process_group "$frontend_pid"
- stop_process_group "$backend_pid"
- deadline=$((SECONDS + 5))
- while (( SECONDS < deadline )); do
- if ! process_group_is_alive "$frontend_pid" && \
- ! process_group_is_alive "$backend_pid"; then
- break
- fi
- sleep 0.2
- done
- if process_group_is_alive "$frontend_pid"; then
- kill -KILL -- "-$frontend_pid" 2>/dev/null || true
- fi
- if process_group_is_alive "$backend_pid"; then
- kill -KILL -- "-$backend_pid" 2>/dev/null || true
- fi
- if [[ -n "$frontend_pid" ]]; then
- wait "$frontend_pid" 2>/dev/null || true
- fi
- if [[ -n "$backend_pid" ]]; then
- wait "$backend_pid" 2>/dev/null || true
- fi
- if [[ -n "$frontend_pid" || -n "$backend_pid" ]]; then
- echo "智保通开发服务已停止。"
- fi
- return "$exit_status"
- }
- http_ready() {
- local url="$1"
- curl --fail --silent --show-error --max-time 2 "$url" >/dev/null 2>&1
- }
- wait_for_services() {
- local deadline=$((SECONDS + 45))
- while (( SECONDS < deadline )); do
- kill -0 "$frontend_pid" 2>/dev/null || \
- die "前端开发服务器启动失败,请检查上方 pnpm 输出。"
- kill -0 "$backend_pid" 2>/dev/null || \
- die "后端开发服务器启动失败,请检查上方 API 与退款 Worker 输出。"
- if http_ready "http://127.0.0.1:5173/" && \
- http_ready "http://127.0.0.1:5174/" && \
- http_ready "http://127.0.0.1:8000/docs"; then
- return 0
- fi
- sleep 0.3
- done
- die "开发服务在 45 秒内未全部就绪,请检查上方启动日志。"
- }
- case "$run_for_seconds" in
- ''|*[!0-9]*)
- die "ZBT_DEV_RUN_SECONDS 必须是非负整数。"
- ;;
- esac
- trap cleanup EXIT
- trap 'exit 130' INT
- trap 'exit 143' TERM
- trap 'exit 129' HUP
- require_command pnpm
- require_command uv
- require_command curl
- require_command lsof
- assert_port_available 5173 "H5"
- assert_port_available 5174 "管理后台"
- assert_port_available 8000 "后端 API"
- echo "正在启动 H5、管理后台和后端 API..."
- # Bash 3.2(macOS 系统自带版本)没有 setsid。临时启用 job control,
- # 可让两个后台任务各自成为独立进程组,清理时连同 Vite/Uvicorn 子进程一起结束。
- set -m
- (
- cd "$project_root/frontend"
- exec pnpm dev
- ) &
- frontend_pid=$!
- (
- cd "$project_root/backend"
- exec uv run zbt-serve
- ) &
- backend_pid=$!
- set +m
- wait_for_services
- echo
- echo "智保通开发服务已全部启动:"
- echo "H5:http://127.0.0.1:5173"
- echo "管理后台:http://127.0.0.1:5174"
- echo "后端 API:http://127.0.0.1:8000/docs"
- echo "按 Ctrl+C 可一次性停止全部服务。"
- ready_at=$SECONDS
- while true; do
- kill -0 "$frontend_pid" 2>/dev/null || \
- die "前端开发服务器意外退出,请检查上方日志。"
- kill -0 "$backend_pid" 2>/dev/null || \
- die "后端开发服务器意外退出,请检查上方日志。"
- if (( run_for_seconds > 0 && SECONDS - ready_at >= run_for_seconds )); then
- break
- fi
- sleep 0.5
- done
|