From ca894dfd4d561a5f324edd58776a0a017a933f73 Mon Sep 17 00:00:00 2001 From: dekun Date: Wed, 24 Jun 2026 11:59:02 +0800 Subject: [PATCH] =?UTF-8?q?deploy:=20=E4=B8=80=E9=94=AE=E9=83=A8=E7=BD=B2?= =?UTF-8?q?=E5=86=85=E7=BD=AE=20locale/=E6=97=B6=E5=8C=BA/SimNow=20?= =?UTF-8?q?=E5=89=8D=E7=BD=AE=E6=8E=A2=E6=B5=8B=E4=B8=8E=20CTP=20=E9=AA=8C?= =?UTF-8?q?=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- .env.example | 2 +- deploy.sh | 143 ++++++++++++++++++++++++++++++++++++++++++++----- docs/DEPLOY.md | 23 +++++--- 3 files changed, 149 insertions(+), 19 deletions(-) diff --git a/.env.example b/.env.example index 66d0efd..0f9f43b 100644 --- a/.env.example +++ b/.env.example @@ -26,7 +26,7 @@ CTP_AUTO_RECONNECT=true SIMNOW_USER= SIMNOW_PASSWORD= SIMNOW_BROKER_ID=9999 -# 7×24 环境示例(以 SimNow 官网最新为准) +# 7×24 / 日盘前置(deploy.sh 会自动 nc 探测并写入可用线路) SIMNOW_TD_ADDRESS=tcp://180.168.146.187:10201 SIMNOW_MD_ADDRESS=tcp://180.168.146.187:10211 SIMNOW_APP_ID=simnow_client_test diff --git a/deploy.sh b/deploy.sh index b136014..1e78317 100644 --- a/deploy.sh +++ b/deploy.sh @@ -1,6 +1,14 @@ #!/usr/bin/env bash -# 国内期货监控系统 - Ubuntu 一键部署 +# 国内期货监控系统 - Ubuntu 一键部署 / 更新 # root 用户 | 目录 /opt/qihuo | 端口 6600 | PM2 +# +# 已内置修复(避免重复踩坑): +# - vnpy_ctp 编译:build-essential python3-dev pkg-config +# - CTP 登录崩溃:zh_CN.GB18030 + zh_CN.UTF-8 locale +# - 时区:Asia/Shanghai(与 SimNow 交易时段一致) +# - SimNow 前置:自动探测可用线路并写入 .env +# - PM2 环境变量:LANG/LC_ALL(见 ecosystem.config.cjs) +# - .env 缺项补全:SIMNOW_ENV、CTP_AUTO_RECONNECT set -euo pipefail @@ -8,12 +16,81 @@ APP_DIR="/opt/qihuo" REPO_URL="https://git.bz121.com/dekun/qihuo.git" SERVICE_NAME="qihuo" +# SimNow 前置候选(按优先级;部署时自动 nc 探测) +SIMNOW_FRONTS=( + "182.254.243.31:30001:30011" + "180.168.146.187:10201:10211" + "180.168.146.187:10130:10131" + "218.202.237.33:10203:10213" +) + if [ "$(id -u)" -ne 0 ]; then echo "请使用 root 用户运行: sudo bash deploy.sh" exit 1 fi +probe_tcp() { + local host="$1" port="$2" + if command -v nc &>/dev/null; then + nc -z -w 3 "$host" "$port" &>/dev/null + return $? + fi + timeout 3 bash -c "echo >/dev/tcp/${host}/${port}" 2>/dev/null +} + +ensure_locale_gen() { + local pat="$1" + if [ -f /etc/locale.gen ] && grep -q "^# ${pat}" /etc/locale.gen; then + sed -i "s/^# ${pat}/${pat}/" /etc/locale.gen + fi +} + +ensure_env_key() { + local file="$1" key="$2" val="$3" + if [ ! -f "$file" ]; then + return + fi + if grep -q "^${key}=" "$file"; then + return + fi + echo "${key}=${val}" >>"$file" + echo " .env 补全: ${key}=${val}" +} + +pick_simnow_front() { + local host td_port md_port + for entry in "${SIMNOW_FRONTS[@]}"; do + IFS=: read -r host td_port md_port <<<"$entry" + if probe_tcp "$host" "$td_port" && probe_tcp "$host" "$md_port"; then + echo "tcp://${host}:${td_port}|tcp://${host}:${md_port}" + return 0 + fi + echo " SimNow 前置不可达: ${host} ${td_port}/${md_port}" >&2 + done + return 1 +} + +update_simnow_front_in_env() { + local env_file="$1" + local picked td md + picked="$(pick_simnow_front)" || { + echo "警告: 未能探测到可用 SimNow 前置,请手动编辑 ${env_file}(见 docs/SIMNOW.md)" + return 1 + } + td="${picked%%|*}" + md="${picked##*|}" + echo "==> SimNow 可用前置: TD=${td} MD=${md}" + if grep -q "^SIMNOW_TD_ADDRESS=" "$env_file"; then + sed -i "s|^SIMNOW_TD_ADDRESS=.*|SIMNOW_TD_ADDRESS=${td}|" "$env_file" + sed -i "s|^SIMNOW_MD_ADDRESS=.*|SIMNOW_MD_ADDRESS=${md}|" "$env_file" + else + echo "SIMNOW_TD_ADDRESS=${td}" >>"$env_file" + echo "SIMNOW_MD_ADDRESS=${md}" >>"$env_file" + fi +} + echo "==> 检查系统依赖..." +export DEBIAN_FRONTEND=noninteractive apt-get update -qq need_install() { @@ -26,14 +103,28 @@ need_install python3 python3 need_install python3-venv python3-venv need_install git git -# vnpy_ctp 在 Linux 上需本地编译(Meson + pkg-config 查找 python3-dev) -echo "==> 安装 vnpy_ctp 编译依赖与 CTP 中文 locale..." -apt-get install -y build-essential python3-dev pkg-config locales -# CTP C++ API 登录回调需要 zh_CN.GB18030(vnpy/vnpy_ctp#24) -sed -i '/^# zh_CN.GB18030/s/^# //' /etc/locale.gen 2>/dev/null || true -sed -i '/^# zh_CN.UTF-8/s/^# //' /etc/locale.gen 2>/dev/null || true +echo "==> 安装 vnpy_ctp 编译依赖..." +apt-get install -y build-essential python3-dev pkg-config locales netcat-openbsd + +echo "==> 配置时区 Asia/Shanghai..." +if command -v timedatectl &>/dev/null; then + timedatectl set-timezone Asia/Shanghai || true +fi + +echo "==> 配置 CTP 所需 locale(zh_CN.GB18030 等)..." +ensure_locale_gen "zh_CN.GB18030 GB18030" +ensure_locale_gen "zh_CN.UTF-8 UTF-8" +ensure_locale_gen "en_US.UTF-8 UTF-8" locale-gen zh_CN.GB18030 zh_CN.UTF-8 en_US.UTF-8 2>/dev/null || locale-gen update-locale LANG=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8 2>/dev/null || true +export LANG=zh_CN.UTF-8 +export LC_ALL=zh_CN.UTF-8 + +if ! locale -a 2>/dev/null | grep -qi gb18030; then + echo "错误: zh_CN.GB18030 未生成,CTP 连接后会崩溃" + exit 1 +fi +echo " locale OK: $(locale -a 2>/dev/null | grep -i gb18030 | head -1)" if ! command -v pm2 &>/dev/null; then echo "==> 安装 PM2..." @@ -64,33 +155,61 @@ echo "==> Python 虚拟环境与依赖..." if [ ! -d "$APP_DIR/venv" ]; then python3 -m venv "$APP_DIR/venv" fi +# shellcheck disable=SC1091 source "$APP_DIR/venv/bin/activate" pip install --upgrade pip -q pip install -r "$APP_DIR/requirements.txt" python -c "from vnpy_ctp import CtpGateway; print('vnpy_ctp OK')" +echo "==> 配置 .env..." if [ ! -f "$APP_DIR/.env" ]; then - echo "==> 生成 .env(请编辑 ADMIN_PASSWORD 后重启)..." cp "$APP_DIR/.env.example" "$APP_DIR/.env" RAND_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))") sed -i "s/change-this-to-a-random-secret-key/${RAND_KEY}/" "$APP_DIR/.env" + echo " 已生成 .env,请编辑 SIMNOW_USER / ADMIN_PASSWORD" fi +ensure_env_key "$APP_DIR/.env" "SIMNOW_ENV" "实盘" +ensure_env_key "$APP_DIR/.env" "CTP_AUTO_RECONNECT" "true" +ensure_env_key "$APP_DIR/.env" "SIMNOW_BROKER_ID" "9999" +ensure_env_key "$APP_DIR/.env" "SIMNOW_APP_ID" "simnow_client_test" +ensure_env_key "$APP_DIR/.env" "SIMNOW_AUTH_CODE" "0000000000000000" +update_simnow_front_in_env "$APP_DIR/.env" || true mkdir -p "$APP_DIR/logs" +echo "==> 验证 CTP 环境..." +if grep -q "^SIMNOW_USER=.\+" "$APP_DIR/.env" 2>/dev/null && \ + grep -q "^SIMNOW_PASSWORD=.\+" "$APP_DIR/.env" 2>/dev/null; then + set +e + python "$APP_DIR/scripts/test_simnow.py" + CTP_TEST=$? + set -e + if [ "$CTP_TEST" -ne 0 ]; then + echo "警告: SimNow 连接测试未通过,请检查 .env 账号与网络(见 docs/SIMNOW.md)" + else + echo " SimNow CTP 连接测试通过" + fi +else + echo " 跳过 CTP 测试(未配置 SIMNOW_USER / SIMNOW_PASSWORD)" +fi + echo "==> PM2 启动/重启服务..." cd "$APP_DIR" -pm2 delete "$SERVICE_NAME" 2>/dev/null || true -pm2 start ecosystem.config.cjs +if pm2 describe "$SERVICE_NAME" &>/dev/null; then + pm2 restart ecosystem.config.cjs --update-env +else + pm2 start ecosystem.config.cjs +fi pm2 save echo "" echo "==========================================" echo " 部署完成" echo " 目录: ${APP_DIR}" -echo " 用户: root" +echo " 时区: $(date +%Z) $(date '+%Y-%m-%d %H:%M:%S')" echo " 端口: 6600" -echo " 访问: http://<服务器IP>:6600" +echo " 访问: http://$(hostname -I 2>/dev/null | awk '{print $1}'):6600" echo " 日志: pm2 logs ${SERVICE_NAME}" +echo " SimNow 注册: docs/SIMNOW.md" echo " 开机自启: pm2 startup && pm2 save" echo "==========================================" diff --git a/docs/DEPLOY.md b/docs/DEPLOY.md index 2c37d6f..c6a4c64 100644 --- a/docs/DEPLOY.md +++ b/docs/DEPLOY.md @@ -46,14 +46,20 @@ bash deploy.sh `deploy.sh` 会自动完成: -1. 安装 `python3`、`python3-venv`、`git`、`nodejs`、`npm`、`pm2` -2. `git pull` 或 `git clone` 到 `/opt/qihuo` -3. 创建虚拟环境 `venv` 并 `pip install -r requirements.txt` -4. 首次生成 `.env`(随机 `SECRET_KEY`) -5. `pm2 start ecosystem.config.cjs` 并 `pm2 save` +1. 安装系统依赖:`python3`、`git`、`build-essential`、`python3-dev`、`pkg-config`、`locales`、`netcat-openbsd`、`pm2` +2. **时区**设为 `Asia/Shanghai`(与 SimNow 交易时段一致) +3. **locale**:生成 `zh_CN.GB18030`、`zh_CN.UTF-8`(CTP 登录必需,缺则进程崩溃) +4. `git pull` 或 `git clone` 到 `/opt/qihuo` +5. 创建/保留虚拟环境 `venv`,`pip install -r requirements.txt`,验证 `vnpy_ctp` +6. 首次生成 `.env`,并补全 `SIMNOW_ENV=实盘`、`CTP_AUTO_RECONNECT=true` 等缺项 +7. **自动探测 SimNow 前置**(`nc` 测端口),写入可用的 `SIMNOW_TD/MD_ADDRESS`(优先 `182.254.243.31`,其次 `180.168.146.187`) +8. 若已配置 SimNow 账号,运行 `scripts/test_simnow.py` 验证连接 +9. `pm2 restart --update-env` 或首次 `pm2 start`,并 `pm2 save` 部署完成后访问:`http://<服务器IP>:6600` +> 再次部署只需 `cd /opt/qihuo && bash deploy.sh`,无需手工装 locale 或改前置地址。 + --- ## 手动部署 @@ -62,7 +68,12 @@ bash deploy.sh ```bash apt update -apt install -y python3 python3-venv python3-pip python3-dev pkg-config git nodejs npm build-essential +apt install -y python3 python3-venv python3-pip python3-dev pkg-config git nodejs npm build-essential locales netcat-openbsd +timedatectl set-timezone Asia/Shanghai +sed -i '/^# zh_CN.GB18030/s/^# //' /etc/locale.gen +sed -i '/^# zh_CN.UTF-8/s/^# //' /etc/locale.gen +locale-gen zh_CN.GB18030 zh_CN.UTF-8 +update-locale LANG=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8 npm install -g pm2 ```