diff --git a/.env.example b/.env.example index ce94b9e..f89da12 100644 --- a/.env.example +++ b/.env.example @@ -1,32 +1,59 @@ -# MySQL +# ============================================================ +# Sino-MCI 通用环境配置模板 +# +# 推荐用法: +# cp .env.local .env # 本机开发,连接 localhost 基础设施 +# cp .env.remote .env # 本机开发,连接远程基础设施(如 192.168.1.173) +# +# 注意: +# 1. .env 文件不会被提交到仓库(已在 .gitignore 中忽略)。 +# 2. scripts/local-up.sh 启动 docker-compose 时,会自动把 host 覆盖为容器名, +# 因此无论 .env 中是 localhost 还是远程 IP,docker-compose 内部都能正确互联。 +# 3. 若手动执行 docker compose,请确保当前 .env 中的 MYSQL_HOST/REDIS_HOST/ +# RABBITMQ_HOST 指向可访问地址,或使用 local-up.sh 启动。 +# ============================================================ + +# -------------------------------------------------- +# 基础设施端口与账号 +# -------------------------------------------------- +MYSQL_PORT=3306 MYSQL_ROOT_PASSWORD=root MYSQL_DATABASE=sino_mci MYSQL_USER=root -MYSQL_PORT=3306 -# Redis REDIS_PORT=6379 -# RabbitMQ RABBITMQ_DEFAULT_USER=guest RABBITMQ_DEFAULT_PASS=guest RABBITMQ_AMQP_PORT=5672 RABBITMQ_MGMT_PORT=15672 -# SeaweedFS SEAWEEDFS_MASTER_PORT=9333 SEAWEEDFS_S3_PORT=8888 -# Application ports +# -------------------------------------------------- +# 应用服务端口 +# -------------------------------------------------- MCI_SERVER_PORT=8080 ADMIN_WEB_PORT=80 CHANNEL_SERVICE_PORT=8000 -# JVM options for mci-server -JAVA_OPTS=-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 - -# Spring profile +# -------------------------------------------------- +# Spring Boot / JVM +# -------------------------------------------------- SPRING_PROFILES_ACTIVE=dev +JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0" -# OpenAI API key (optional) -OPENAI_API_KEY=your-openai-api-key-here +# -------------------------------------------------- +# Channel Service +# -------------------------------------------------- +MAX_RETRY=5 +BASE_RETRY_DELAY_SECONDS=5 +EVENT_RETENTION_DAYS=10 +EVENT_PUBLISH_INTERVAL_SECONDS=5 + +# -------------------------------------------------- +# OpenAI(可选) +# -------------------------------------------------- +OPENAI_API_KEY= +OPENAI_BASE_URL=https://api.openai.com diff --git a/.env.local b/.env.local new file mode 100644 index 0000000..c5e52f1 --- /dev/null +++ b/.env.local @@ -0,0 +1,50 @@ +# ============================================================ +# Sino-MCI 本地开发环境(本机启动后端/前端时加载) +# 使用方式:cp .env.local .env +# 说明:应用服务连接本机 localhost 上的基础设施 +# ============================================================ + +# 基础设施连接地址 +MYSQL_HOST=localhost +REDIS_HOST=localhost +RABBITMQ_HOST=localhost +SEAWEEDFS_HOST=localhost + +# 基础设施端口与账号 +MYSQL_PORT=3306 +MYSQL_ROOT_PASSWORD=root +MYSQL_DATABASE=sino_mci +MYSQL_USER=root + +REDIS_PORT=6379 + +RABBITMQ_DEFAULT_USER=guest +RABBITMQ_DEFAULT_PASS=guest +RABBITMQ_AMQP_PORT=5672 +RABBITMQ_MGMT_PORT=15672 + +SEAWEEDFS_MASTER_PORT=9333 +SEAWEEDFS_S3_PORT=8888 + +# 应用服务端口 +MCI_SERVER_PORT=8080 +ADMIN_WEB_PORT=80 +CHANNEL_SERVICE_PORT=8000 + +# 应用服务地址 +MCI_SERVER_URL=http://localhost:8080/msg-platform +VITE_API_BASE_URL=http://localhost:8080 + +# Spring Boot / JVM +SPRING_PROFILES_ACTIVE=dev +JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0" + +# Channel Service +MAX_RETRY=5 +BASE_RETRY_DELAY_SECONDS=5 +EVENT_RETENTION_DAYS=10 +EVENT_PUBLISH_INTERVAL_SECONDS=5 + +# OpenAI(可选) +OPENAI_API_KEY= +OPENAI_BASE_URL=https://api.openai.com diff --git a/.env.remote b/.env.remote new file mode 100644 index 0000000..c6221ca --- /dev/null +++ b/.env.remote @@ -0,0 +1,50 @@ +# ============================================================ +# Sino-MCI 远程基础设施环境(本机启动后端/前端时加载) +# 使用方式:cp .env.remote .env +# 说明:只启动本机应用服务,连接远程 MySQL/Redis/RabbitMQ/SeaweedFS +# ============================================================ + +# 基础设施连接地址 +MYSQL_HOST=192.168.1.173 +REDIS_HOST=192.168.1.173 +RABBITMQ_HOST=192.168.1.173 +SEAWEEDFS_HOST=192.168.1.173 + +# 基础设施端口与账号 +MYSQL_PORT=3306 +MYSQL_ROOT_PASSWORD=root +MYSQL_DATABASE=sino_mci +MYSQL_USER=root + +REDIS_PORT=6379 + +RABBITMQ_DEFAULT_USER=guest +RABBITMQ_DEFAULT_PASS=guest +RABBITMQ_AMQP_PORT=5672 +RABBITMQ_MGMT_PORT=15672 + +SEAWEEDFS_MASTER_PORT=9333 +SEAWEEDFS_S3_PORT=8888 + +# 应用服务端口 +MCI_SERVER_PORT=8080 +ADMIN_WEB_PORT=80 +CHANNEL_SERVICE_PORT=8000 + +# 应用服务地址 +MCI_SERVER_URL=http://192.168.1.173:8080/msg-platform +VITE_API_BASE_URL=http://192.168.1.173:8080 + +# Spring Boot / JVM +SPRING_PROFILES_ACTIVE=dev +JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0" + +# Channel Service +MAX_RETRY=5 +BASE_RETRY_DELAY_SECONDS=5 +EVENT_RETENTION_DAYS=10 +EVENT_PUBLISH_INTERVAL_SECONDS=5 + +# OpenAI(可选) +OPENAI_API_KEY= +OPENAI_BASE_URL=https://api.openai.com diff --git a/admin-web/vite.config.ts b/admin-web/vite.config.ts index d9bc44a..2af6d62 100644 --- a/admin-web/vite.config.ts +++ b/admin-web/vite.config.ts @@ -1,25 +1,30 @@ -import { defineConfig } from 'vite' +import { defineConfig, loadEnv } from 'vite' import vue from '@vitejs/plugin-vue' import { resolve } from 'path' // https://vite.dev/config/ -export default defineConfig({ - plugins: [vue()], - resolve: { - alias: { - '@': resolve(__dirname, 'src'), - }, - }, - server: { - port: 5173, - proxy: { - '/msg-platform': { - target: 'http://localhost:8080', - changeOrigin: true, +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), '') + const apiBaseUrl = env.VITE_API_BASE_URL || 'http://localhost:8080' + + return { + plugins: [vue()], + resolve: { + alias: { + '@': resolve(__dirname, 'src'), }, }, - }, - build: { - outDir: 'dist', - }, + server: { + port: parseInt(env.VITE_ADMIN_WEB_PORT || '5173', 10), + proxy: { + '/msg-platform': { + target: apiBaseUrl, + changeOrigin: true, + }, + }, + }, + build: { + outDir: 'dist', + }, + } }) diff --git a/backend/mci-server/src/main/resources/application-dev.yml b/backend/mci-server/src/main/resources/application-dev.yml index 0d24ad6..18b2195 100644 --- a/backend/mci-server/src/main/resources/application-dev.yml +++ b/backend/mci-server/src/main/resources/application-dev.yml @@ -1,19 +1,19 @@ spring: datasource: - url: jdbc:mysql://localhost:3306/sino_mci?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true - username: root - password: root + url: jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:sino_mci}?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true + username: ${MYSQL_USER:root} + password: ${MYSQL_ROOT_PASSWORD:root} driver-class-name: com.mysql.cj.jdbc.Driver data: redis: - host: localhost - port: 6379 + host: ${REDIS_HOST:localhost} + port: ${REDIS_PORT:6379} database: 0 rabbitmq: - host: localhost - port: 5672 - username: guest - password: guest + host: ${RABBITMQ_HOST:localhost} + port: ${RABBITMQ_AMQP_PORT:5672} + username: ${RABBITMQ_DEFAULT_USER:guest} + password: ${RABBITMQ_DEFAULT_PASS:guest} ai: openai: api-key: ${OPENAI_API_KEY:} diff --git a/docker-compose.yml b/docker-compose.yml index f97a97a..883d97c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -89,15 +89,17 @@ services: - "${MCI_SERVER_PORT:-8080}:8080" environment: - SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE:-dev} - - SPRING_DATASOURCE_URL=jdbc:mysql://mci-mysql:3306/${MYSQL_DATABASE:-sino_mci}?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true - - SPRING_DATASOURCE_USERNAME=${MYSQL_USER:-root} - - SPRING_DATASOURCE_PASSWORD=${MYSQL_ROOT_PASSWORD:-root} - - SPRING_DATA_REDIS_HOST=mci-redis - - SPRING_DATA_REDIS_PORT=6379 - - SPRING_RABBITMQ_HOST=mci-rabbitmq - - SPRING_RABBITMQ_PORT=5672 - - SPRING_RABBITMQ_USERNAME=${RABBITMQ_DEFAULT_USER:-guest} - - SPRING_RABBITMQ_PASSWORD=${RABBITMQ_DEFAULT_PASS:-guest} + - MYSQL_HOST=${MYSQL_HOST:-mci-mysql} + - MYSQL_PORT=${MYSQL_PORT:-3306} + - MYSQL_DATABASE=${MYSQL_DATABASE:-sino_mci} + - MYSQL_USER=${MYSQL_USER:-root} + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root} + - REDIS_HOST=${REDIS_HOST:-mci-redis} + - REDIS_PORT=${REDIS_PORT:-6379} + - RABBITMQ_HOST=${RABBITMQ_HOST:-mci-rabbitmq} + - RABBITMQ_AMQP_PORT=${RABBITMQ_AMQP_PORT:-5672} + - RABBITMQ_DEFAULT_USER=${RABBITMQ_DEFAULT_USER:-guest} + - RABBITMQ_DEFAULT_PASS=${RABBITMQ_DEFAULT_PASS:-guest} - JAVA_OPTS=${JAVA_OPTS:--XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0} - OPENAI_API_KEY=${OPENAI_API_KEY:-} networks: @@ -141,12 +143,12 @@ services: ports: - "${CHANNEL_SERVICE_PORT:-8000}:8000" environment: - - MCI_SERVER_URL=http://mci-server:8080/msg-platform + - MCI_SERVER_URL=${MCI_SERVER_URL:-http://mci-server:8080/msg-platform} - EVENT_STORE_PATH=/app/data/events.db - - MAX_RETRY=5 - - BASE_RETRY_DELAY_SECONDS=5 - - EVENT_RETENTION_DAYS=10 - - EVENT_PUBLISH_INTERVAL_SECONDS=5 + - MAX_RETRY=${MAX_RETRY:-5} + - BASE_RETRY_DELAY_SECONDS=${BASE_RETRY_DELAY_SECONDS:-5} + - EVENT_RETENTION_DAYS=${EVENT_RETENTION_DAYS:-10} + - EVENT_PUBLISH_INTERVAL_SECONDS=${EVENT_PUBLISH_INTERVAL_SECONDS:-5} - OPENAI_API_KEY=${OPENAI_API_KEY:-} volumes: - channel-service-data:/app/data diff --git a/scripts/local-up.sh b/scripts/local-up.sh index 811fc2f..33f2a41 100755 --- a/scripts/local-up.sh +++ b/scripts/local-up.sh @@ -17,6 +17,17 @@ MCI_SERVER_PORT=${MCI_SERVER_PORT:-8080} ADMIN_WEB_PORT=${ADMIN_WEB_PORT:-80} CHANNEL_SERVICE_PORT=${CHANNEL_SERVICE_PORT:-8000} +# Docker Compose 内部连接必须使用容器名,覆盖 .env 中的 host 配置。 +# 当用本脚本启动应用服务时,应用与基础设施在同一 Docker 网络。 +dc() { + MYSQL_HOST=mci-mysql \ + REDIS_HOST=mci-redis \ + RABBITMQ_HOST=mci-rabbitmq \ + SEAWEEDFS_HOST=mci-seaweedfs \ + MCI_SERVER_URL=http://mci-server:8080/msg-platform \ + docker compose "$@" +} + WITH_APPS="" BUILD_FLAG="" INIT_DATA="" @@ -60,7 +71,7 @@ wait_for_healthy() { echo "Waiting for $service to become healthy..." while [ "$retry_count" -lt "$max_retries" ]; do - container_name=$(docker compose -p sino-mci --profile infra --profile app ps -q "$service" 2>/dev/null | head -n1 || true) + container_name=$(dc -p sino-mci --profile infra --profile app ps -q "$service" 2>/dev/null | head -n1 || true) if [ -n "$container_name" ]; then local status @@ -76,7 +87,7 @@ wait_for_healthy() { done echo "$service did not become healthy within $((max_retries * 2)) seconds." - docker compose -p sino-mci --profile infra --profile app logs --tail=50 "$service" + dc -p sino-mci --profile infra --profile app logs --tail=50 "$service" return 1 } @@ -102,7 +113,7 @@ init_database() { } echo "Starting infrastructure services..." -docker compose -p sino-mci --profile infra up -d $BUILD_FLAG +dc -p sino-mci --profile infra up -d $BUILD_FLAG wait_for_healthy mysql 60 wait_for_healthy redis 30 @@ -123,7 +134,7 @@ echo "SeaweedFS: http://localhost:${SEAWEEDFS_MASTER_PORT}" if [ -n "$WITH_APPS" ]; then echo "Starting application services..." # Include infra profile so that depends_on conditions can resolve. - docker compose -p sino-mci --profile infra --profile app up -d $BUILD_FLAG + dc -p sino-mci --profile infra --profile app up -d $BUILD_FLAG wait_for_healthy mci-server 90 wait_for_healthy channel-service 60 diff --git a/scripts/run-local.sh b/scripts/run-local.sh new file mode 100755 index 0000000..f196593 --- /dev/null +++ b/scripts/run-local.sh @@ -0,0 +1,71 @@ +#!/bin/bash +set -euo pipefail + +# Sino-MCI 本地开发启动脚本 +# 自动加载项目根目录 .env 文件中的环境变量,然后启动后端或前端。 +# +# 用法: +# ./scripts/run-local.sh backend +# ./scripts/run-local.sh frontend +# ./scripts/run-local.sh channel +# +# 切换环境: +# cp .env.local .env # 本地基础设施 +# cp .env.remote .env # 远程基础设施(如 192.168.1.173) + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$SCRIPT_DIR/.." +cd "$PROJECT_ROOT" + +if [ ! -f .env ]; then + echo ".env file not found. Creating from .env.local ..." + cp .env.local .env +fi + +# 加载 .env(忽略注释与空行) +set -o allexport +# shellcheck source=/dev/null +source .env +set +o allexport + +echo "Loaded environment from: $PROJECT_ROOT/.env" +echo " MYSQL_HOST=${MYSQL_HOST:-localhost}" +echo " REDIS_HOST=${REDIS_HOST:-localhost}" +echo " RABBITMQ_HOST=${RABBITMQ_HOST:-localhost}" +echo " MCI_SERVER_URL=${MCI_SERVER_URL:-http://localhost:8080/msg-platform}" +echo " VITE_API_BASE_URL=${VITE_API_BASE_URL:-http://localhost:8080}" +echo "" + +SERVICE=${1:-} +if [ -z "$SERVICE" ]; then + echo "Usage: $0 " + exit 1 +fi + +case "$SERVICE" in + backend|server) + cd backend/mci-server + echo "Starting mci-server with Maven..." + ./mvnw spring-boot:run -Dspring-boot.run.profiles=dev + ;; + frontend|web) + cd admin-web + echo "Starting admin-web with Vite..." + npm run dev + ;; + channel|channel-service) + cd channel-service + echo "Starting channel-service..." + if [ ! -d .venv ]; then + python3 -m venv .venv + fi + source .venv/bin/activate + pip install -r requirements.txt -q + python -m app.main + ;; + *) + echo "Unknown service: $SERVICE" + echo "Usage: $0 " + exit 1 + ;; +esac