根因: Flutter Web CanvasKit 渲染器会从 fonts.gstatic.com 按需加载 CJK 字体 woff2 子集,在中国网络环境下完全不可达。 修复: 启动参数添加 --web-renderer html,使用浏览器原生 CSS font-face 机制加载本地字体文件,无需外网访问。 面向中国国内市场,不依赖任何外部 CDN。
204 lines
5.9 KiB
Bash
204 lines
5.9 KiB
Bash
#!/bin/bash
|
|
# 暖记开发环境启动脚本 — 自动清理旧进程 + 启动后端和前端
|
|
#
|
|
# 用法:
|
|
# ./scripts/dev.sh # 启动全部 (后端+管理端+学生端)
|
|
# ./scripts/dev.sh backend # 只启动后端
|
|
# ./scripts/dev.sh admin # 只启动管理端前端 (React, port 5174)
|
|
# ./scripts/dev.sh app # 只启动学生端 Flutter (port 8080)
|
|
# ./scripts/dev.sh stop # 停止所有服务
|
|
|
|
set -e
|
|
|
|
# ===== 配置 =====
|
|
BACKEND_PORT=3000
|
|
ADMIN_PORT=5174 # 管理端前端 (React + Ant Design)
|
|
APP_PORT=8080 # 学生端前端 (Flutter Web)
|
|
PG_HOST="localhost"
|
|
PG_PORT=5432
|
|
PG_USER="postgres"
|
|
PG_PASS="123123"
|
|
PG_DB="nuanji"
|
|
|
|
# ===== 颜色 =====
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
|
log_ok() { echo -e "${GREEN}[OK]${NC} $1"; }
|
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
log_err() { echo -e "${RED}[ERROR]${NC} $1"; }
|
|
|
|
# ===== 清理端口占用的进程 =====
|
|
kill_port() {
|
|
local port=$1
|
|
local name=$2
|
|
local pids=$(netstat -ano 2>/dev/null | grep ":${port}.*LISTENING" | awk '{print $5}' | sort -u)
|
|
|
|
if [ -n "$pids" ]; then
|
|
for pid in $pids; do
|
|
if [ "$pid" != "0" ]; then
|
|
taskkill //F //PID "$pid" 2>/dev/null && \
|
|
log_ok "已停止 ${name} (PID: ${pid}, 端口: ${port})" || \
|
|
log_warn "无法停止 ${name} (PID: ${pid})"
|
|
fi
|
|
done
|
|
else
|
|
log_info "${name} 端口 ${port} 空闲"
|
|
fi
|
|
}
|
|
|
|
stop_all() {
|
|
log_info "停止所有暖记服务..."
|
|
kill_port $BACKEND_PORT "后端"
|
|
kill_port $ADMIN_PORT "管理端前端"
|
|
kill_port $APP_PORT "学生端前端"
|
|
taskkill //F //IM erp-server.exe 2>/dev/null && log_ok "已停止 erp-server.exe" || true
|
|
log_ok "所有服务已停止"
|
|
}
|
|
|
|
# ===== 检查依赖 =====
|
|
check_deps() {
|
|
log_info "检查依赖..."
|
|
|
|
# PostgreSQL
|
|
if PGPASSWORD=$PG_PASS /d/postgreSQL/bin/psql.exe -U $PG_USER -h $PG_HOST -p $PG_PORT \
|
|
-d $PG_DB -c "SELECT 1" > /dev/null 2>&1; then
|
|
log_ok "PostgreSQL 已连接 (${PG_DB}@${PG_HOST}:${PG_PORT})"
|
|
else
|
|
log_err "无法连接 PostgreSQL (${PG_DB}@${PG_HOST}:${PG_PORT})"
|
|
log_err "请确保 PostgreSQL 已启动且数据库 ${PG_DB} 存在"
|
|
exit 1
|
|
fi
|
|
|
|
# Redis
|
|
if redis-cli ping > /dev/null 2>&1; then
|
|
log_ok "Redis 已连接"
|
|
else
|
|
log_err "无法连接 Redis (localhost:6379)"
|
|
exit 1
|
|
fi
|
|
|
|
# Flutter
|
|
if D:/flutter/bin/flutter.bat --version > /dev/null 2>&1; then
|
|
log_ok "Flutter SDK 可用"
|
|
else
|
|
log_err "Flutter SDK 不可用"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# ===== 启动后端 =====
|
|
start_backend() {
|
|
log_info "清理旧后端进程..."
|
|
kill_port $BACKEND_PORT "后端"
|
|
taskkill //F //IM erp-server.exe 2>/dev/null || true
|
|
sleep 1
|
|
|
|
log_info "编译并启动后端 (diary feature)..."
|
|
cd /g/nj
|
|
ERP__DATABASE__URL="postgres://${PG_USER}:${PG_PASS}@${PG_HOST}:${PG_PORT}/${PG_DB}" \
|
|
ERP__REDIS__URL="redis://localhost:6379" \
|
|
ERP__JWT__SECRET="nuanji-dev-jwt-secret-2024-warm-notes-hmac-key-32b" \
|
|
ERP__AUTH__SUPER_ADMIN_PASSWORD="admin123" \
|
|
ERP__WECHAT__APPID="wx_dev_placeholder" \
|
|
ERP__WECHAT__SECRET="wx_dev_secret_placeholder" \
|
|
ERP__WECHAT__DEV_MODE="true" \
|
|
ERP__CRYPTO__KEK="0000000000000000000000000000000000000000000000000000000000000000" \
|
|
cargo run -p erp-server --features diary &
|
|
|
|
# 等待后端就绪
|
|
log_info "等待后端就绪..."
|
|
for i in $(seq 1 30); do
|
|
if curl -s "http://localhost:${BACKEND_PORT}/api/v1/health" > /dev/null 2>&1; then
|
|
log_ok "后端已就绪 → http://localhost:${BACKEND_PORT}"
|
|
return 0
|
|
fi
|
|
sleep 2
|
|
done
|
|
log_err "后端启动超时"
|
|
return 1
|
|
}
|
|
|
|
# ===== 启动管理端前端 (React + Ant Design) =====
|
|
start_admin() {
|
|
log_info "清理旧管理端进程..."
|
|
kill_port $ADMIN_PORT "管理端前端"
|
|
sleep 1
|
|
|
|
log_info "启动管理端前端 (React + Vite)..."
|
|
cd /g/nj/apps/web
|
|
pnpm dev &
|
|
|
|
log_info "等待管理端就绪..."
|
|
for i in $(seq 1 20); do
|
|
if curl -s -o /dev/null "http://localhost:${ADMIN_PORT}" 2>/dev/null; then
|
|
log_ok "管理端已就绪 → http://localhost:${ADMIN_PORT}"
|
|
return 0
|
|
fi
|
|
sleep 2
|
|
done
|
|
log_err "管理端启动超时"
|
|
return 1
|
|
}
|
|
|
|
# ===== 启动学生端 Flutter Web =====
|
|
start_app() {
|
|
log_info "清理旧学生端进程..."
|
|
kill_port $APP_PORT "学生端前端"
|
|
sleep 1
|
|
|
|
log_info "编译并启动 Flutter Web..."
|
|
cd /g/nj/app
|
|
# --web-renderer html: 使用浏览器原生渲染,避免 CanvasKit 从 Google Fonts CDN 加载中文字体
|
|
# 面向中国国内市场,不可依赖外部 CDN
|
|
D:/flutter/bin/flutter.bat run -d chrome --web-port=$APP_PORT --web-renderer html &
|
|
|
|
log_info "等待学生端就绪..."
|
|
for i in $(seq 1 30); do
|
|
if curl -s -o /dev/null "http://localhost:${APP_PORT}" 2>/dev/null; then
|
|
log_ok "学生端已就绪 → http://localhost:${APP_PORT}"
|
|
return 0
|
|
fi
|
|
sleep 3
|
|
done
|
|
log_err "学生端启动超时"
|
|
return 1
|
|
}
|
|
|
|
# ===== 主流程 =====
|
|
case "${1:-all}" in
|
|
stop)
|
|
stop_all
|
|
;;
|
|
backend)
|
|
check_deps
|
|
start_backend
|
|
;;
|
|
admin)
|
|
check_deps
|
|
start_admin
|
|
;;
|
|
app)
|
|
check_deps
|
|
start_app
|
|
;;
|
|
all)
|
|
check_deps
|
|
start_backend
|
|
start_admin
|
|
start_app
|
|
log_ok "=== 暖记开发环境已启动 ==="
|
|
log_ok "后端 API: http://localhost:${BACKEND_PORT}"
|
|
log_ok "管理端: http://localhost:${ADMIN_PORT} (admin/admin123)"
|
|
log_ok "学生端: http://localhost:${APP_PORT}"
|
|
;;
|
|
*)
|
|
echo "用法: $0 [all|backend|admin|app|stop]"
|
|
exit 1
|
|
;;
|
|
esac
|