From a36f0a016a808d259d04cb7988bcf8448925b26e Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Thu, 6 Aug 2026 09:41:34 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=9C=8D=E5=8A=A1=E7=AB=AF?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 6 ++++-- apps/services/sys/nats/core.go | 9 ++++++++- apps/services/sys/nats/nats.go | 24 +++++++++++++++++++++--- deploy/admin/README.md | 18 +++++++++--------- deploy/admin/prod-deploy.sh | 11 +++++++---- deploy/console/README.md | 10 +++++----- deploy/console/prod-deploy.sh | 11 +++++++---- deploy/registry-profiles.sh | 11 ++++++----- 8 files changed, 67 insertions(+), 33 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 39f261d5..cbf8d5d3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -72,10 +72,12 @@ cd deploy/console && ./prod-deploy.sh 0.2.1 yunyan # 第 2 个参数选生产 - `lingpu` → `registry.lingpu.net`(灵谱香港机) **生产镜像名也按公司区分**,映射同在 `registry-profiles.sh` 的 `prod_image_name()`(build 与 deploy 共用一份,防漂移): -云雁沿用历史名 `yunyan-admin`/`console-a11`/`voitrans-a11`;灵谱用 `starpivot-*` 新名。 +admin/console 两家都用 `starpivot-*`(与 `docker-compose.yml` 里写死的 image/container_name 一致); +只有云雁的 app 仍是 `voitrans-a11`(上海机正跑着 7 个该镜像的容器,改名 compose 会认不出)。 admin/console 的 `prod-deploy.sh` 已改为**多生产环境档案**(`env_profile()`,同 `dev-deploy.sh` 的写法): -- `yunyan` → 47.253.88.66(美国阿里云),目录 `/home/work/yunyan-admin`、`/home/work/console-a11` +- `yunyan` → 139.224.212.237(上海阿里云,与业务后端 app 同机),目录 `/home/work/starpivot/{admin,console}`,密钥 `~/Documents/key/aliyun_shanghai.pem` + (2026-08-05 由美国机 47.253.88.66 迁来;美国机上的 `yunyan-admin`/`console-a11` 容器仍在跑,待验证后停,脚本已不再指向它) - `lingpu` → 8.166.114.65(广州阿里云),目录 `/home/work/starpivot/{admin,console}`,密钥 `~/.ssh/lingpu-gz.pem` `dev-deploy.sh <环境>` 只做镜像部署:构建推送 → 远端 pull 重启,**不上传** 服务器上的真实 `.env` / `confs/*.yaml`(只下发 `*.example` 模板)。 diff --git a/apps/services/sys/nats/core.go b/apps/services/sys/nats/core.go index fd9bc467..edd47593 100644 --- a/apps/services/sys/nats/core.go +++ b/apps/services/sys/nats/core.go @@ -30,7 +30,14 @@ func OnInit(config map[string]interface{}, opt ...Option) (err error) { if options, err = newOptions(config, opt...); err != nil { return } - defsys, err = newSys(options) + // 注意:必须先取到具体类型再判空后赋值。若直接 defsys, err = newSys(...), + // 失败时会把 (*natsSys)(nil) 装进接口,使 defsys != nil 成立, + // 后续 Conn()/JetStream() 的 nil 判断全部失效并在方法内 panic。 + var sys *natsSys + if sys, err = newSys(options); err != nil { + return + } + defsys = sys return } diff --git a/apps/services/sys/nats/nats.go b/apps/services/sys/nats/nats.go index 8eeb076c..1e6d6627 100644 --- a/apps/services/sys/nats/nats.go +++ b/apps/services/sys/nats/nats.go @@ -43,22 +43,40 @@ func (this *natsSys) init() (err error) { return } -func (this *natsSys) Conn() *gonats.Conn { return this.conn } +// 以下方法一律做 nil 接收者防护:NewSys 失败时调用方若把 (nil, err) 存进 ISys 接口, +// 接口不为 nil 但接收者为 nil,读字段会直接 panic。宁可降级返回 nil / ErrNotReady。 +func (this *natsSys) Conn() *gonats.Conn { + if this == nil { + return nil + } + return this.conn +} -func (this *natsSys) JetStream() gonats.JetStreamContext { return this.js } +func (this *natsSys) JetStream() gonats.JetStreamContext { + if this == nil { + return nil + } + return this.js +} func (this *natsSys) Publish(subject string, data []byte) error { + if this == nil || this.js == nil { + return ErrNotReady + } _, err := this.js.Publish(subject, data) return err } func (this *natsSys) PublishAsync(subject string, data []byte) error { + if this == nil || this.js == nil { + return ErrNotReady + } _, err := this.js.PublishAsync(subject, data) return err } func (this *natsSys) Close() { - if this.conn != nil { + if this != nil && this.conn != nil { _ = this.conn.Drain() } } diff --git a/deploy/admin/README.md b/deploy/admin/README.md index 33471679..7f4c137d 100644 --- a/deploy/admin/README.md +++ b/deploy/admin/README.md @@ -1,8 +1,8 @@ # admin 管理前端部署 -把 Nuxt 4 SSR 管理后台 **admin** 打成镜像 `yunyan-admin`,推送到私有仓库 +把 Nuxt 4 SSR 管理后台 **admin** 打成镜像 `starpivot-admin`,推送到私有仓库 `docker-registry.ideapsound.com`,再到目标机拉取重启。它本身不连数据库,只通过 -`CONSOLE_BACKEND` 把 `/web/api`、`/console/api` 代理到同网络的 **console** 后端(容器名 `console-a11`)。 +`CONSOLE_BACKEND` 把 `/web/api`、`/console/api` 代理到同网络的 **console** 后端(容器名 `starpivot-console`)。 > 约定(与 console 一致):仓库**只提交模板**(`*.example`)。部署脚本会把**模板 + `docker-compose.yml`** 同步到服务器, > 但**绝不上传/覆盖**你在服务器上填好的真实 `.env`(首次需登录服务器照模板复制并填值)。admin 没有 yaml 配置,全靠 `.env`。 @@ -12,7 +12,7 @@ | 文件 | 说明 | |---|---| | `../../apps/admin/Dockerfile` | 镜像构建(Nuxt SSR:pnpm build → node server),构建上下文 = `apps/admin/` | -| `docker-compose.yml` | 运行镜像 `yunyan-admin`,`.env` 注入容器,只 `expose 3000`(经反代对外) | +| `docker-compose.yml` | 运行镜像 `starpivot-admin`,`.env` 注入容器,只 `expose 3000`(经反代对外) | | `env/env.example` | 运行时环境**模板**(只一份,不分环境);真实 `.env` 被 gitignore | | `dev-deploy.sh` | **开发**:构建 dev 镜像 → 推送 → 远端拉取重启(固定 tag `dev-latest`) | | `prod-build.sh` | **生产①**:构建带版本号镜像并推送(`:版本` + `:latest`),不部署 | @@ -27,7 +27,7 @@ ```bash cd deploy/admin cp env/env.example .env # 填真实值(至少 CONSOLE_BACKEND) -docker compose up -d # 本机起 yunyan-admin +docker compose up -d # 本机起 starpivot-admin ``` ### 服务器准备(dev / prod,一次性) @@ -36,7 +36,7 @@ docker compose up -d # 本机起 yunyan-admin (`cp env.example .env`,dev-deploy 缺真实文件时也会打印该命令): ``` -<部署目录>/ # dev / prod 均默认 /home/work/yunyan-admin +<部署目录>/ # dev / prod 均默认 /home/work/starpivot/admin ├── docker-compose.yml # 从本目录拷贝 └── .env # 照 env.example 填(CONSOLE_BACKEND / TAG / DOCKER_NETWORK) ``` @@ -48,7 +48,7 @@ cd deploy/admin ./dev-deploy.sh lingpu # 开发:发布到灵谱测试环境(构建 dev-latest → 推送 → 远端拉取重启) ./dev-deploy.sh yunyan # 开发:发布到云雁测试环境 ./dev-deploy.sh # 不带参数 → 交互选择测试环境 -./prod-build.sh 0.1.1 # 生产①:构建并推送 yunyan-admin:0.1.1 (+latest) +./prod-build.sh 0.1.1 yunyan # 生产①:构建并推送 starpivot-admin:0.1.1 (+latest);第2参数选公司 ./prod-deploy.sh 0.1.1 lingpu # 生产②:部署 0.1.1 到指定生产环境(yunyan|lingpu),省略环境则交互选 ./prod-deploy.sh rollback lingpu # 生产回滚到上一版本(与当前版本来回切换) ``` @@ -61,11 +61,11 @@ cd deploy/admin - **部署目标与凭据**:写死在各脚本顶部。`dev-deploy.sh` 的 `env_profile()`(每个测试环境一套 `REGISTRY/DEV_HOST/DEV_KEY/DEV_DIR`,现有 `lingpu`、`yunyan`;新增环境加一段 case 并写进 `ENVS`)、`prod-deploy.sh` 的 `env_profile()`(每个生产环境一套 `REGISTRY/IMAGE_NAME/DEPLOY_HOST/DEPLOY_KEY/DEPLOY_DIR`,现有 `yunyan`、`lingpu`;新增环境加一段 case 并写进 `PROD_ENVS`); 仓库账号 `admin` / 密码已写死;`DEPLOY_KEY` 默认 `/Users/liwei/Documents/key/yunyan-us.pem`(prod)。 - ⚠️ 本目录脚本纳入 git,**公开仓库前务必清空** `*_HOST` 与 `REGISTRY_PASS`。 -- **后端地址**:`.env` 的 `CONSOLE_BACKEND`,须指向同网络的 console 容器,默认 `http://console-a11:8080`。 +- **后端地址**:`.env` 的 `CONSOLE_BACKEND`,须指向同网络的 console 容器,默认 `http://starpivot-console:8080`。 ## 端口与反代 admin HTTP `3000`(容器内,只 `expose` 不 publish)。须由 **1panel 反代 / NPM** 把后台域名 -(如 `console.yunyanservice.xyz`)转发到 `yunyan-admin:3000`。admin 再在内部把 `/web/api`、`/console/api` -代理到 `console-a11:8080`,所以**单域名**即可跑通整套(浏览器 → 反代 → yunyan-admin → console-a11)。 +(如 `console.yunyanservice.xyz`)转发到 `starpivot-admin:3000`。admin 再在内部把 `/web/api`、`/console/api` +代理到 `starpivot-console:8080`,所以**单域名**即可跑通整套(浏览器 → 反代 → starpivot-admin → starpivot-console)。 admin 与 console 须在同一 `DOCKER_NETWORK`(默认 `1panel-network`)。 diff --git a/deploy/admin/prod-deploy.sh b/deploy/admin/prod-deploy.sh index e835d874..a0ebaae5 100755 --- a/deploy/admin/prod-deploy.sh +++ b/deploy/admin/prod-deploy.sh @@ -40,11 +40,14 @@ env_profile() { DEPLOY_USER=root case "$DEPLOY_ENV" in yunyan) - ENV_DESC="云雁生产环境 · 美国阿里云" + # 2026-08-05 起 admin 生产目标改为上海机(与业务后端 app 同机)。 + # 旧目标:美国机 47.253.88.66,其上 yunyan-admin:0.1.1 容器仍在跑,待新环境验证后再停; + # 需要临时回连那台时,把下面三行换成 47.253.88.66 / yunyan-us.pem / /home/work/yunyan-admin。 + ENV_DESC="云雁生产环境 · 上海阿里云" registry_profile yunyan || return 1 # 镜像仓库:云雁 - DEPLOY_HOST="47.253.88.66" # ← 云雁生产服务器 IP - DEPLOY_KEY=/Users/liwei/Documents/key/yunyan-us.pem - DEPLOY_DIR="/home/work/yunyan-admin" # 远端部署目录(其中 docker-compose.yml/.env 由你自己维护) + DEPLOY_HOST="139.224.212.237" # ← 云雁生产服务器 IP(上海) + DEPLOY_KEY=/Users/liwei/Documents/key/aliyun_shanghai.pem + DEPLOY_DIR="/home/work/starpivot/admin" # 远端部署目录(其中 docker-compose.yml/.env 由你自己维护) ;; lingpu) ENV_DESC="灵谱生产环境 · 广州阿里云 (lingpu-gz)" diff --git a/deploy/console/README.md b/deploy/console/README.md index 771c07ee..1ee4e267 100644 --- a/deploy/console/README.md +++ b/deploy/console/README.md @@ -1,6 +1,6 @@ # console 管理后台部署 -把单服务 **console**(lego single 服务,维护 app_registry、跨应用统计)打成镜像 `console-a11`, +把单服务 **console**(lego single 服务,维护 app_registry、跨应用统计)打成镜像 `starpivot-console`, 推送到私有仓库 `docker-registry.ideapsound.com`,再到目标机拉取重启。依赖 PostgreSQL / Redis / NATS 由目标环境共享提供(在 `.env` 里填地址/容器名),不在此 compose 内搭建。 @@ -13,7 +13,7 @@ |---|---| | `../../apps/services/Dockerfile.console` | 镜像构建(编译 console 二进制 + entrypoint + 内置 consoleweb 静态),构建上下文 = `apps/services/` | | `../../apps/services/entrypoint.console.sh` | 容器入口 | -| `docker-compose.yml` | 运行镜像 `console-a11`,挂 `./confs`、`./log`,`.env` 注入容器(此文件由你放到服务器) | +| `docker-compose.yml` | 运行镜像 `starpivot-console`,挂 `./confs`、`./log`,`.env` 注入容器(此文件由你放到服务器) | | `confs/console.yaml.example` | console 配置**模板**;真实 `console.yaml` 被 gitignore,密钥/端点用 `${VAR}` 由 `.env` 注入 | | `env/env.example` | 运行时环境**模板**(只一份,不分环境);真实 `.env` 被 gitignore | | `dev-deploy.sh` | **开发**:构建 dev 镜像 → 推送 → 远端拉取重启(固定 tag `dev-latest`) | @@ -30,7 +30,7 @@ cd deploy/console cp env/env.example .env # 填真实值 cp confs/console.yaml.example confs/console.yaml -docker compose --env-file .env up -d # 本机起 console-a11 +docker compose --env-file .env up -d # 本机起 starpivot-console ``` ### 服务器准备(dev / prod,一次性) @@ -39,7 +39,7 @@ docker compose --env-file .env up -d # 本机起 console-a11 (`cp env.example .env`、`cp confs/console.yaml.example confs/console.yaml`,dev-deploy 缺真实文件时也会打印这两条命令): ``` -<部署目录>/ # dev 默认 /home/work/console-a11,prod 默认 /opt/yunyan/console +<部署目录>/ # dev / prod 均默认 /home/work/starpivot/console ├── docker-compose.yml # 从本目录拷贝 ├── .env # 照 env/env.example 填(POSTGRES_DSN/REDIS_*/NATS_URL/ │ # CONSOLE_TOKEN_KEY/CONSOLE_ADMIN_*/FIELD_ENCRYPT_KEY 等) @@ -54,7 +54,7 @@ cd deploy/console ./dev-deploy.sh lingpu # 开发:发布到灵谱测试环境(构建 dev-latest → 推送 → 远端拉取重启) ./dev-deploy.sh yunyan # 开发:发布到云雁测试环境 ./dev-deploy.sh # 不带参数 → 交互选择测试环境 -./prod-build.sh 0.1.1 # 生产①:构建并推送 console-a11:0.1.1 (+latest) +./prod-build.sh 0.1.1 yunyan # 生产①:构建并推送 starpivot-console:0.1.1 (+latest);第2参数选公司 ./prod-deploy.sh 0.1.1 lingpu # 生产②:部署 0.1.1 到指定生产环境(yunyan|lingpu),省略环境则交互选 ./prod-deploy.sh rollback lingpu # 生产回滚到上一版本(与当前版本来回切换) ``` diff --git a/deploy/console/prod-deploy.sh b/deploy/console/prod-deploy.sh index 40578de2..d0d08929 100755 --- a/deploy/console/prod-deploy.sh +++ b/deploy/console/prod-deploy.sh @@ -40,11 +40,14 @@ env_profile() { DEPLOY_USER=root case "$DEPLOY_ENV" in yunyan) - ENV_DESC="云雁生产环境 · 美国阿里云" + # 2026-08-05 起 console 生产目标改为上海机(与业务后端 app 同机)。 + # 旧目标:美国机 47.253.88.66,其上 console-a11:0.1.1 容器仍在跑,待新环境验证后再停; + # 需要临时回连那台时,把下面三行换成 47.253.88.66 / yunyan-us.pem / /home/work/console-a11。 + ENV_DESC="云雁生产环境 · 上海阿里云" registry_profile yunyan || return 1 # 镜像仓库:云雁 - DEPLOY_HOST="47.253.88.66" # ← 云雁生产服务器 IP - DEPLOY_KEY=/Users/liwei/Documents/key/yunyan-us.pem - DEPLOY_DIR="/home/work/console-a11" # 远端部署目录(其中 docker-compose.yml/.env/confs 由你自己维护) + DEPLOY_HOST="139.224.212.237" # ← 云雁生产服务器 IP(上海) + DEPLOY_KEY=/Users/liwei/Documents/key/aliyun_shanghai.pem + DEPLOY_DIR="/home/work/starpivot/console" # 远端部署目录(其中 docker-compose.yml/.env/confs 由你自己维护) ;; lingpu) ENV_DESC="灵谱生产环境 · 广州阿里云 (lingpu-gz)" diff --git a/deploy/registry-profiles.sh b/deploy/registry-profiles.sh index 85e654fb..3e844464 100644 --- a/deploy/registry-profiles.sh +++ b/deploy/registry-profiles.sh @@ -41,15 +41,16 @@ registry_profile() { # 生产镜像名按公司区分。prod-build.sh 与 prod-deploy.sh 都调它,避免两处各写一份映射后漂移 # ——构建推的名字和部署拉的名字对不上,报错要到远端 docker pull 才暴露。 # -# 云雁:沿用历史命名(yunyan-admin / console-a11 / voitrans-a11)。服务器上正跑着这些同名容器, -# 改名会让 compose 认不出既有容器,故不动它。 -# 灵谱:全新部署,用 starpivot-* 新命名,与灵谱测试环境(dev-deploy.sh)保持一致。 +# admin/console:两家都用 starpivot-* 新命名,与 docker-compose.yml 里写死的 image/container_name +# 一致。云雁的 admin/console 生产目标已于 2026-08-05 迁到上海机(那台没有历史容器), +# 旧名 yunyan-admin / console-a11 只留在待下线的美国机 47.253.88.66 上,不再由脚本部署。 +# app:云雁仍用 voitrans-a11——上海机正跑着 7 个该镜像的容器,改名会让 compose 认不出既有容器。 # # 用法:IMAGE_NAME="$(prod_image_name lingpu admin)" prod_image_name() { # $1=公司 $2=服务(admin|console|app) case "${1:-}/${2:-}" in - yunyan/admin) printf 'yunyan-admin' ;; - yunyan/console) printf 'console-a11' ;; + yunyan/admin) printf 'starpivot-admin' ;; + yunyan/console) printf 'starpivot-console' ;; yunyan/app) printf 'voitrans-a11' ;; lingpu/admin|lingpu/console|lingpu/app) printf 'starpivot-%s' "$2" ;; *)