You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

43 KiB

阿龙测试服 → 阿龙正式服 · 服务端上线落地文档

日期:2026-09-11 方向:8.133.166.29(阿龙测试,跑 dev-latest)→ 47.116.104.181(阿龙正式,跑 0.1.4) 范围:app(gateway/home/api/mcp/timer 单镜像)、console、admin 三个服务端。客户端 App 本轮不发版。 数据口径:用户数据不迁;只搬「平台能力配置」,产品/设备/批次等业务台账不搬。


0. 先看结论

本文档的前提全部是 2026-09-11 在两台机上实测得到的,不是照着脚本推的:

  1. 正式服没有任何用户数据 —— user=0、userdevice=0、echomeet_record=0、payorder 空。 所以「用户数据不要部署过去」这条天然满足,不需要任何排除规则、不需要脱敏。
  2. 正式服代码停在 8-18 的 0.1.4,这三周的能力一个都没有:记忆中心(拾忆)、会员与算力、 身份证实名核验、设备表合表(license_<pid> → device_mac)、预签名直传 OSS、 MCP 令牌与会议纪要检索、发版配置拆表(app_release/channel_app)。
  3. 正式服有三处地基性错配(§2),不先修的话新能力部上去也是静默失效——不报错、看板恒为 0、 按应用作用域的配置全部退化成全局默认。
  4. 正式服的 App 配置表(业务库 config)是空的,产品台账也是空的。光升镜像,App 端拿不到 任何第三方能力配置,设备也绑不上。

所以这次不是「升个版本」,是三件事叠加:升镜像 + 补齐配置 + 搬平台能力配置。

建议本次版本号 0.2.0(正式现为 0.1.4)。


1. 两边现状实测对照

1.1 服务与版本

测试 8.133.166.29 正式 47.116.104.181
app 容器 / 目录 starpivot-app · /home/work/starpivot/app ym-a11 · /home/work/ym-a11
console starpivot-console · /home/work/starpivot/console 同名 · /home/work/starpivot/console
admin starpivot-admin · /home/work/starpivot/admin 同名 · /home/work/starpivot/admin
镜像 tag dev-latest(今日构建) 0.1.4(2026-08-18/16 部署)
镜像仓库 registry.ymaikj.com(实体就在测试机上,账号 admin) 同一个仓库
对外域名 ym-dev / console-dev / mcp-dev.ymaikj.com ym / console / mcp.ymaikj.com DNS 都已就绪;但 NPM 里只建了 ym 与 console 两条反代,mcp 那条待补(§8.3)
依赖组件 etcd / mysql / redis / postgres / nats 均在机上 同左,齐全

1.2 数据库

两边各自一套,互不相通:

测试 正式
console 主库(PG) starpivot_console(容器 postgres,角色 admin) 同名同角色
业务库(MySQL) starpivot-app starpivot-ym

console 主库表差异(正式缺,升级后由 console 启动自动建): app_goods、app_release、chip_vendor、oem_factory、device_mac、settlement_month。 正式库里还留着老表 factory、factory_deliverynote、license、third_svc_config、third_svc_region_override。

业务库表差异(正式缺,升级后由业务服务启动自动建): memory_item、memory_report、useridverify。

行数对照(关键几张):

表 库 测试 正式 处置
user / userdevice / echomeet_record 业务库 25 / — / 252 0 / 0 / 0 不迁(本来就空)
config(App 自有配置,下发客户端的 env) 业务库 42 0 要搬(§7.2)
app_module_config 业务库 36 36 不动
svc_config(第三方服务池) console 主库 10 8 要搬差额(缺实名核验 2 条)
svc_region_override console 主库 114 112 随上条一起
sys_service_config(平台自用:对象存储/邮件) console 主库 1(email) 0 要搬
echomeet_orch / _setting console 主库 4 / 1 4 / 1 核对引用的服务 id 是否都存在
agent_config / call_translate_rule / mcp console 主库 0 0 无需处理
global_config console 主库 473 473 一致,不动
echomeet_template console 主库 287 1189 正式偏多(疑似重复 seed),不搬,见 §12
product / channel / production_batch / solution_provider console 主库 4 / 2 / 8 / 3 0 / 0 / 0 / 0 按你的决定不搬,后果见 §12
device_mac console 主库 有表 无表 升级后自动建,内容不搬

2. 阻塞项:上线前必须先处理的三件事

P0-1 ⚠️ 应用身份三处对不上(最关键)

实测值:

位置 测试 正式
.env 的 ANALYZE_APP_NAME EAIMAR-TEST starpivot-app-dev(照抄模板没改)
app_registry.name EAIMAR-TEST EAIMAR(国内)
app_registry.app_name EAIMAR-TEST EAIMAR
app_service_config.app_name(18 行) EAIMAR-TEST starpivot-app-dev

测试服三处同值,是对的;正式服三处两两不同,等于一处都没对上。后果(两条都静默,不报错):

  • 统计快照被 console 拒收 —— 判定点是 model_registry.go 的 getAppByName,查的是 name=?。 被拒时只在 console 日志留一行 console.stat: 快照应用未在 console 注册,未落库,数据滞留业务侧 Redis, 后台看板恒为 0 且不报任何错。
  • 按应用作用域解析的配置(渠道分发、user_getagents_v2、user_getthirdsvcs_v2、会议编排) 全部退化成全局默认。

修法(把正式服统一成 EAIMAR,一次改四处,改完必须整体重启):

-- console 主库 starpivot_console
UPDATE app_registry        SET name = 'EAIMAR', app_name = 'EAIMAR' WHERE id = 1;
UPDATE app_service_config  SET app_name = 'EAIMAR' WHERE app_name = 'starpivot-app-dev';
-- 顺带:channel_app 里那条 Voitrans-Test 是测试残留,确认后删
-- DELETE FROM channel_app WHERE app_name = 'Voitrans-Test';
# 正式机 /home/work/ym-a11/.env
ANALYZE_APP_NAME=EAIMAR

⚠️ name 同时是后台应用切换器上的显示名,改完会从「EAIMAR(国内)」变成「EAIMAR」。这是必须付的代价: 两列不同值时,ANALYZE_APP_NAME 填哪个都只对一半(填 app_name → 配置读得到、统计被拒; 填 name → 统计能落库、作用域配置全退化)。

⚠️ 改 app_service_config.app_name 这一步不能漏,那 18 行是该应用的基础设施/运行配置, app_name 对不上就全读不到。

P0-2 ⚠️ 会员与算力等改动还在工作区,没有提交

当前分支 along,工作区有大量未提交改动,其中服务端部分包括:

  • 新增:comm/compute.go、modules/console/api_compute.go、modules/user/api_getcompute.go、apps/admin/app/pages/compute.vue
  • 修改:user 模块 8 个文件、console 6 个文件、echomeet、pay、api,以及 pb/*.pb.go 与对应 .proto

测试机上跑的 dev-latest 就是这份工作区代码构建的。 生产镜像必须从一个确定的提交构建, 否则回滚时无从对照。上线前先把这批改动提交(是否提交/如何组织由你决定,我不代你 commit)。

建议顺手打个 tag:git tag v0.2.0 && git push origin v0.2.0,与镜像 tag 对齐。

P0-3 ⚠️ 部署脚本里的 SSH 私钥路径在本机不存在

三个 prod-deploy.sh(app / console / admin)的 along 段都写死:

DEPLOY_KEY="/Users/liwei1dao/liwei/密钥/along/releae-guangzhou.pem"

本机实际只有 ~/Documents/keys/loginscre.pem(已验证可连两台机)。二选一:

# 方案 A(推荐,不改 git 跟踪文件):做一个目录软链,一次性
sudo mkdir -p "/Users/liwei1dao/liwei/密钥/along"
sudo ln -sf ~/Documents/keys/loginscre.pem "/Users/liwei1dao/liwei/密钥/along/releae-guangzhou.pem"
# 方案 B:把三个脚本的 along 段改成可被环境变量覆盖(与 dev-deploy.sh 的写法一致)
#   DEPLOY_KEY="${DEPLOY_KEY:-/Users/liwei1dao/liwei/密钥/along/releae-guangzhou.pem}"
# 然后:DEPLOY_KEY=~/Documents/keys/loginscre.pem ./prod-deploy.sh ...

3. 阶段 A · 备份(必做,且不可跳过)

本次升级会在正式服触发不可逆的启动迁移:

迁移 触发点 不可逆之处
license_<pid> 分表 → device_mac 合表 console 启动 migrateLicenseToDeviceMac 建新表并搬数据(旧表刻意不删,可回退)
product DROP devicetype / scanuuid 列 console + api 启动 列直接删掉
chip_vendor DROP cid 列 console 启动 列直接删掉
旧额度桶折算成算力 home 启动 migrateLegacyBucketsToCompute(config 表打 COMPUTE_LEGACY_MIGRATED 标记) 一次性折算
echomeet_record 建 ngram 全文索引 echomeet 启动 大表上耗时(正式表为空,本次瞬时)
ssh -i ~/Documents/keys/loginscre.pem root@47.116.104.181

# console 主库
docker exec postgres pg_dump -U admin -d starpivot_console --no-owner --no-privileges \
  -f /tmp/console-pre020.sql
docker cp postgres:/tmp/console-pre020.sql /root/backup-console-pre020-$(date +%Y%m%d-%H%M).sql

# 业务库
docker exec mysql sh -c 'mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --single-transaction \
  --databases "starpivot-ym"' > /root/backup-ym-pre020-$(date +%Y%m%d-%H%M).sql

# 三份运行时配置(脚本不会覆盖它们,但改坏了要能还原)
tar czf /root/backup-conf-pre020-$(date +%Y%m%d-%H%M).tgz \
  /home/work/ym-a11/.env /home/work/ym-a11/confs \
  /home/work/starpivot/console/.env /home/work/starpivot/console/confs \
  /home/work/starpivot/admin/.env

备份文件拉回本地一份再继续。


4. 阶段 B · 构建镜像

三个服务各自构建,都推到阿龙仓库(第 2 个参数 along 不能省,省了会默认推云雁仓库, 到远端 docker pull 才暴露):

cd deploy/app     && ./prod-build.sh 0.2.0 along
cd ../console     && ./prod-build.sh 0.2.0 along
cd ../admin       && ./prod-build.sh 0.2.0 along

构建完确认三个 tag 都在仓库里:

for img in starpivot-app starpivot-console starpivot-admin; do
  echo -n "$img: "
  curl -s -u admin:<仓库密码> "https://registry.ymaikj.com/v2/$img/tags/list" | grep -o '0\.2\.0'
done

5. 阶段 C · 补齐 .env 与 confs(部署前做)

部署脚本只下发 *.example 模板,绝不覆盖服务器上的真实 .env / confs/*.yaml, 所以下面这些必须手工在正式机上补。以下每一项都是实测出来的、正式机当前确实缺的。

5.1 /home/work/ym-a11/.env

键 现状 改成 为什么
ANALYZE_APP_NAME starpivot-app-dev EAIMAR 见 P0-1
MCP_TOKEN_KEY 缺 一个与 GATEWAY_TOKEN_KEY 不同的强随机串 MCP 会话令牌要经百炼之手,不能用能开全站的那把钥匙签;留空则 EMAI 里查会议纪要/待办的工具全部不可用
ID_HASH_SALT 缺 强随机串,一旦启用不要再改 实名认证身份证指纹盐;留空不会退化成裸哈希,而是不写指纹(后台查不了同证件多账号)
CLUSTER_TAG starpivot-app-dev 建议 eaimar-prod 现值与测试机同名。两机 etcd 各自独立,暂不致命,但语义上是错的;改了要整体重启
REDIS_KEY_PREFIX EAIMAR 不动 已是正确的应用身份
API_SITE_NAME Voitrans(开发) 建议改成正式站点名 只影响 api 后台显示
MCP_ADDR http://localhost:7300 https://mcp.ymaikj.com(先建反代,见 §8.3) 这个值会作为 SSE BaseURL 下发给百炼,localhost 对外不可达

.env 通过 compose 的 env_file 整体注入容器,所以新增键不需要改任何 yaml,重建容器即生效。 docker restart 不重读 env_file,必须 up -d --force-recreate(部署脚本已经是这么做的)。

5.2 /home/work/ym-a11/confs/gateway.yaml

未登录白名单补两行:

      - user_getchannelapps      # 复数这个才是客户端在调的;少了它游客登录入口在 App 里永远不显示
      - echomeet_alibackcall     # 阿里录音文件转写的异步回调,外部服务发来、没有 token

⚠️ user_getchannelapps 仓库模板里已经有了,正式机那份是 8-18 的旧文件所以缺。 ⚠️ echomeet_alibackcall 仓库模板里也还没有(测试机是手工加的)。会议转写用阿里 ASR 就必须放行, 否则回调进不来、转写退化成客户端轮询,且静默。 ⚠️ 该接口自身没有签名校验,只靠阿里下发的 UUID task_id 不可猜作保护——正式环境建议后续补回调密钥。

5.3 /home/work/ym-a11/confs/home.yaml

  user:
    McpTokenKey: "${MCP_TOKEN_KEY:-}"   # 与 mcp.yaml 同值;留空则 user_getmcptoken 直接报错

  memory: # 记忆中心(拾忆)——正式机整段缺失,要新增
    Debug: ${LOG_DEBUG:-false}
    max_report_process: 3
    report_min_items: 3
    report_cron_week: "0 30 0 ? * MON"
    report_cron_month: "0 30 0 1 * ?"
    report_stale_sec: 900
    meeting_extract: true

直接照 deploy/app/confs/home.yaml.example 对齐更稳妥。 ⚠️ wordfilter.WorldFile 保持注释状态 —— 词库 txt 不在 git 里,列了却没有文件,home 会 panic、 entrypoint 杀容器无限重启,而表象是 api 报 Table 'xxx.userdevice' doesn't exist。

5.4 /home/work/ym-a11/confs/mcp.yaml

  mcp:
    TokenKey: "${GATEWAY_TOKEN_KEY:-}"    # 校验登录 JWT,须与 gateway 同值
    McpTokenKey: "${MCP_TOKEN_KEY:-}"     # 校验 MCP 专用令牌,须与 home.yaml 的 user 段同值
    Groups:
      CHINA:
        # ... 原有工具保留,补这四个:
        - search_meeting_notes
        - get_memory_items
        - get_memory_stats
        - get_memory_report

5.5 console / admin

console/.env 与 admin/.env 的键集合两边一致,无需新增。只确认一条:

⚠️ console/.env 与 ym-a11/.env 的 FIELD_ENCRYPT_KEY 必须完全相同。 console 后台把邮件/短信/支付/第三方密钥以 AES-256-GCM 写进库,业务服务启动时用这把 key 解密。 不一致时解密会静默失败并把密文当明文用——例如把密文当 Resend API Key 发出去, 表现为 401 API key is invalid,而后台显示配置填好了,极难排查。

# 在正式机上比指纹(不打印明文)
ssh root@47.116.104.181 \
 'a=$(grep ^FIELD_ENCRYPT_KEY= /home/work/ym-a11/.env | cut -d= -f2-); \
  b=$(grep ^FIELD_ENCRYPT_KEY= /home/work/starpivot/console/.env | cut -d= -f2-); \
  [ "$a" = "$b" ] && echo "✓ 一致" || echo "✗ 不一致,必须先统一"'

这一步也是 §7 能不能直接搬 svc_config 的前提:那张表的 fields 里是密文。 如果正式服的 key 与测试服不同,从测试库导过去的密文在正式服解不开——那就只能在后台逐项重填, 不能走 SQL 搬迁。所以在 §7 之前,先按同样方式比一次测试机与正式机的 FIELD_ENCRYPT_KEY。


6. 阶段 D · 部署

顺序:console → app → admin。console 先起,因为建表/迁移和注册表都靠它; admin 只是前端壳,最后起。

# 1) console(会跑 device_mac 合表、DROP devicetype/cid 列等迁移)
cd deploy/console && ./prod-deploy.sh 0.2.0 along
ssh -i ~/Documents/keys/loginscre.pem root@47.116.104.181 \
  'docker logs --tail 200 starpivot-console 2>&1 | grep -iE "migrat|panic|error|拒收"'

# 2) app(五个业务服务,会建 memory_item/memory_report/useridverify 等表)
cd ../app && ./prod-deploy.sh ym along 0.2.0
ssh -i ~/Documents/keys/loginscre.pem root@47.116.104.181 \
  'docker logs --tail 300 ym-a11 2>&1 | grep -iE "panic|fatal|no found file|doesn.t exist"'

# 3) admin
cd ../admin && ./prod-deploy.sh 0.2.0 along

部署脚本会做的事(不用你手动):下发 *.example 模板、按应用改写 compose 的 service 名、 缺失时补传 ip2region_v*.xdb、跑 home.yaml 词库/IP 库前置检查、把 TAG 与 CONTAINER_NAME 写回 .env、 维护 .deploy_tag / .deploy_tag.prev。

⚠️ app 用的是三段参数形式 <应用> <区域> <tag>(ym along 0.2.0), console/admin 是 <tag> <环境>(0.2.0 along),两者参数顺序不同,别写混。

⚠️ .deploy_tag.prev 目前是空的(正式机从没部署过第二个版本),所以 本次部署完成之前 rollback 不可用。本次部完 prev 才会变成 0.1.4。

部完立刻确认迁移结果

-- console 主库
\dt                                  -- 应出现 device_mac / app_release / app_goods / chip_vendor / oem_factory
SELECT count(*) FROM device_mac;     -- 正式服台账为空,这里预期 0,属正常
SELECT column_name FROM information_schema.columns
 WHERE table_name='product' AND column_name IN ('devicetype','scanuuid');   -- 应为 0 行
-- 业务库 starpivot-ym
SHOW TABLES LIKE 'memory%';          -- memory_item / memory_report
SHOW TABLES LIKE 'useridverify';

7. 阶段 E · 搬「平台能力配置」

两边 console 主库互不相通,下面这些要从测试库导到正式库。 先做 §5.5 的 FIELD_ENCRYPT_KEY 比对——不一致就不要走 SQL,改为在后台逐项重填。

7.1 console 主库:第三方服务池与平台服务

正式服相对测试服缺的,实测就是这些:

表 缺什么
svc_config 实名核验两条:idverify_aliyun、idverify_chuanglan(categories=11)
svc_config 的 storage_aliyun_oss 少 domain 字段(测试是 https://oss.ymaikj.com)。不补不报错,但上传后的地址会退成裸 OSS 域名,见 §8.4
svc_region_override 对应的区域覆盖行(114 vs 112)
sys_service_config 整张表空,测试有 email 一条
# 在测试机上导出(这些表的 app_name 实测都是空串=全局作用域,所以不需要改 app_name)
ssh -i ~/Documents/keys/loginscre.pem root@8.133.166.29 \
 "docker exec postgres pg_dump -U admin -d starpivot_console --data-only --no-owner \
    -t svc_config -t svc_region_override -t sys_service_config -f /tmp/svc.sql"
ssh -i ~/Documents/keys/loginscre.pem root@8.133.166.29 'docker cp postgres:/tmp/svc.sql /tmp/svc.sql'
scp -i ~/Documents/keys/loginscre.pem root@8.133.166.29:/tmp/svc.sql /tmp/svc.sql
scp -i ~/Documents/keys/loginscre.pem /tmp/svc.sql root@47.116.104.181:/tmp/svc.sql

⚠️ 不要直接 psql -f 灌进去 —— --data-only 的 dump 是 COPY 全量插入, 会和正式库已有的 8 条 svc_config 主键冲突(或产生重复行)。正确做法是导进临时 schema 再按需合并:

-- 正式库 starpivot_console
CREATE SCHEMA IF NOT EXISTS stg;
-- 把 /tmp/svc.sql 里的 COPY 目标改到 stg(或用 psql -v 指定 search_path 导入),然后:

-- ⚠️ 两张表都有自增主键(svc_config.row_id、svc_region_override.id),
--    必须显式列出列把它排除掉,用 SELECT * 会主键冲突。
--    业务唯一键:svc_config 是 (app_name, id);svc_region_override 是 (app_name, svc_id, region)。

INSERT INTO svc_config
  (app_name, id, name, provider, categories, description, enable, fields, createtime, updatetime)
SELECT s.app_name, s.id, s.name, s.provider, s.categories, s.description, s.enable, s.fields,
       s.createtime, s.updatetime
  FROM stg.svc_config s
 WHERE NOT EXISTS (SELECT 1 FROM svc_config d WHERE d.app_name = s.app_name AND d.id = s.id);

INSERT INTO svc_region_override
  (app_name, svc_id, region, overrides, extra_fields, disabled_keys, updatetime)
SELECT s.app_name, s.svc_id, s.region, s.overrides, s.extra_fields, s.disabled_keys, s.updatetime
  FROM stg.svc_region_override s
 WHERE NOT EXISTS (SELECT 1 FROM svc_region_override d
                    WHERE d.app_name = s.app_name AND d.svc_id = s.svc_id AND d.region = s.region);

-- sys_service_config 的主键 id 是业务串(如 'email'),可以整行插
INSERT INTO sys_service_config
SELECT * FROM stg.sys_service_config s
 WHERE NOT EXISTS (SELECT 1 FROM sys_service_config d WHERE d.id = s.id);

DROP SCHEMA stg CASCADE;

更省事也更稳的替代路径:只有 3 条记录(2 条实名核验 + 1 条 email), 直接在正式后台「第三方服务配置 / 系统配置」里照测试后台重填一遍,凭据从服务商处取。 这样连 FIELD_ENCRYPT_KEY 是否一致都不用管。记录少的时候优先走这条。

⚠️ svc_config 里 categories=11(身份证实名核验)是服务端专用类别, 凭据是云账号主 AK/SK。它不会下发给客户端——拦截点是 svcresolve.go 里的 comm.IsServerOnlySvc 那三行,comm/svcpool_serveronly_test.go 守着。别动那段。

7.2 业务库:config 表(42 条,正式全空)

这是 user_getappconfig 下发给客户端的 env,客户端用 AppConfig.env('X') 读。 正式库为空 = App 端所有第三方能力(Azure 语音、火山、讯飞、COS、EMAI 百炼、Spotify)全无配置。

# 测试机导出整张 config(--where 的引号嵌套太容易写错,改成先全导、导入后再删那一行)
ssh -i ~/Documents/keys/loginscre.pem root@8.133.166.29 \
 'docker exec mysql sh -c '"'"'mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --no-create-info \
    --complete-insert "starpivot-app" config'"'"'' > /tmp/config.sql

scp -i ~/Documents/keys/loginscre.pem /tmp/config.sql root@47.116.104.181:/tmp/config.sql
# 正式机导入后,立刻删掉迁移标记那一行(id=43,group='compute')
#   DELETE FROM config WHERE `group` = 'compute';

导入正式库前逐条过一遍,下面这些要按正式环境改,不能照搬:

分组 键 处置
compute COMPUTE_LEGACY_MIGRATED 不要搬(上面的 --where 已排除)。它是旧额度桶折算的完成标记,搬过去会让正式服跳过折算
COS COS_SECRET_ID/KEY/APP_ID/REGION/BUCKET_NAME 老客户端仍读它直传腾讯云,先保留;新客户端走服务端预签名(走 sys_service_config 的对象存储 / svc_config 的 storage_aliyun_oss)
ALIBABA_BAILIAN_APP_ID / _WORKSPACE_ID EMAI 百炼 正式服若用不同的百炼应用,必须改;代码里有硬编码兜底,配错了不报错、静默用写死的值
AI / AZURE / VOLCANO / VOLC_OPENSPEECH / XunFei 各家密钥 正式与测试用同一套账号就直接搬;要分账号就在后台改
APP APP_AUTHCODE_OPEN / APP_NAVIGATION_MODE 等 按正式的产品策略核一遍

7.3 不需要搬的

  • agent_config、call_translate_rule、mcp —— 两边都是 0 行。
  • global_config —— 两边都是 473 行,一致。
  • third_svc_template —— console 启动会 seed 内置服务商模板,自动补齐。
  • echomeet_orch / echomeet_orch_setting —— 两边行数一样(4 / 1)。 但要核对它引用的服务 id 在正式的 svc_config 里都存在,缺一个那条编排就哑掉:
-- 正式库:把编排里引用的 svc id 拉出来和 svc_config 对一遍
SELECT * FROM echomeet_orch;
SELECT id, name, enable FROM svc_config ORDER BY categories, id;

8. 阶段 F · 后台补配与反代

8.1 会员与算力参数(业务库 config 表,后台「会员与算力」页)

正式服这批键当前全缺,全部允许配 0(0 = 不送 / 不提示),解析只在「键不存在或非数字」时才回默认:

键 含义
COMPUTE_RATE_TRANSLATE / _MEETING 算力 / 分钟
COMPUTE_RATE_AICHAT 算力 / 100 字
COMPUTE_GATE 1=余额不足即拒;0(默认)=只记账不拦
NEWUSER_GIFT_VIPDAYS / NEWUSER_GIFT_COMPUTE 开户礼
VIP_WARN_DAYS / COMPUTE_WARN 客户端提示阈值

⚠️ COMPUTE_GATE 先保持 0(关)。 现在 user_usages 是「用完才上报」,没有预扣, 开了闸门也形同虚设,反而可能误伤。要开闸门得先把预扣做出来。

8.2 对象存储(预签名直传的前提)

正式服 sys_service_config 空,svc_config 里有 storage_aliyun_oss(enable=t)。 配置的唯一读取点是 comm/ossconf.go,取值顺序:系统配置→对象存储(sys_service_config)优先, 回退第三方服务配置→存储(svc_config, provider=aliyun_oss)。

上线后必须实际验一次上传(§9),否则 App 的录音/图片上传会整条报「不支持的上传用途」或 403。

8.3 补上 MCP 的反代与证书(DNS 已就绪,缺的是 NPM 这一步)

正式服的三个域名 DNS 都已经配好并指向 47.116.104.181(2026-09-11 在正式机上 getent hosts 实测)。 但 NPM 里只建了两条 proxy host,mcp 这条还没有:

域名 DNS HTTPS 实际落到哪
ym.ymaikj.com ✅ ✅ 返回 404(网关对 / 的正常响应,真实路由是 /api/:p1/:p2) ym-a11
console.ymaikj.com ✅ ✅ starpivot-admin
mcp.ymaikj.com ✅ ❌ http=000 ssl=1(TLS 握手失败,没有该域名的证书) HTTP 落到 openresty 默认页,没转到 7300

实测依据:/home/work/nginx-proxy-manager/data/nginx/proxy_host/ 下只有 1.conf(console) 与 2.conf(ym), dead_host / redirection_host / stream 三个目录都是空的。

所以只剩三步:

  1. NPM 新建 proxy host:mcp.ymaikj.com → ym-a11:7300,申请证书, 必须勾上 WebSockets Support —— MCP 走 SSE 长连接,默认配置会被缓冲/断开
  2. .env 的 MCP_ADDR=https://mcp.ymaikj.com,重建容器(MCP_ADDR 是下发给百炼的 SSE BaseURL)
  3. 百炼控制台把 MCP server 地址指到这个域名

8.4 域名全清单:服务端不需要再加域名

把所有对外入口过了一遍(2026-09-11 实测),正式服部署不需要新增任何自有服务器域名:

入口 容器:端口 正式域名 状态
业务网关 gateway HTTP ym-a11:7100 ym.ymaikj.com ✅ 已通
管理后台 admin starpivot-admin:3000 console.ymaikj.com ✅ 已通
MCP HTTP/SSE ym-a11:7300 mcp.ymaikj.com DNS ✅,反代待建(§8.3)
api 的 Console 后台 ym-a11:8080 — 不需要对外,测试服也没暴露;admin 走容器内 CONSOLE_BACKEND
gateway/home/timer/mcp/api 的 RPCX 7001–7005 — 集群内部,走 etcd 发现,绝不能对外

对照测试服:starpivot 相关也只有三条 NPM 规则(ym-dev→7100、mcp-dev→7300、console-dev→3000), 其余 gitea / registry / keemir-dev / dev.yimaizn.com / www.ymaikj.com 都是别的项目。正式一一对应,没有遗漏的端口。

但有一个 OSS 域名要注意(不是服务器域名,是 OSS 自定义域名)

oss.ymaikj.com → 8.133.135.83(阿里云 OSS,不指向任何一台业务机)。实测已生效: 根路径 403(列桶被拒,正常)、不存在的 key 返回 404(说明域名确实绑到了桶并能正确路由)。 测试与正式共用同一个桶 ymaioss(上海),所以这个域名正式服直接复用,无需新增、无需改解析。

⚠️ 但正式服的 svc_config.storage_aliyun_oss 里没有 domain 字段(实测只有 bucket=ymaioss、 endpoint=https://oss-cn-shanghai.aliyuncs.com 两项),测试服那条是 domain = https://oss.ymaikj.com。 后果不是报错:comm.AliyunOSSConf.PublicURL() 在 domain 为空时回退成「把预签名 query 去掉的裸 OSS 地址」, 即 https://ymaioss.oss-cn-shanghai.aliyuncs.com/EAIMAR/...。桶是公共读,所以能播能看, 只是所有录音/图片地址都不走 oss.ymaikj.com。§7.1 搬配置时把 domain 这一项一起补上。

客户端相关的辅助域名(本轮不发版,先记账)

这四个域名目前全部解析到测试机 8.133.166.29,而且测试机 NPM 上也没有它们的反代规则 (= 解析了但没服务):

域名 现解析 用途 什么时候必须处理
pay.ymaikj.com 测试机 支付回调。服务端的 notify_url 是客户端下单时传上来的(CreateOrderReq.NotifyUrl),不是服务端配置 正式环境要收款时。届时客户端传的 notify_url 必须能打到正式 gateway 的 pay_wechatnotify / pay_alipaynotify
share.ymaikj.com 测试机 会议分享落地页 正式启用分享时
web.ymaikj.com 测试机 EAIMAR 的用户侧 Web 版:录音同步到云端后在网页上播放/管理/分享。App 里已有真实入口——member_bottom_sheet.dart:97 点了会用内嵌 WebView 打开 https://web.ymaikj.com(meeting_mine_view.dart:95 那条 WEB 只展示、onTap 是空的),41 个语言的文案里也都写了这个地址。但 apps/web 只有 app.vue + index.vue 两个文件,页面内容是「Yunyan / Web 前台(开发中)」,功能没做、从未部署 做完 Web 版时。在那之前 App 里那个入口点开就是空白页/打不开
cdn.ymaikj.com 测试机 没有任何代码在用它。 只出现在两处文字里:comm/ossconf.go:135 的注释举例,和 api_svctemplate.go:299 里后台「存储」表单 domain 字段的提示文案「自定义域名(可选,如 cdn.ymaikj.com;留空用 OSS 源站地址)」。实际在用的是 oss.ymaikj.com 不用管。纯历史占位,解析到测试机没有任何影响

⚠️ 本轮只发服务端、客户端仍指向 ym-dev,所以这四个不阻塞上线。但正式发客户端那一版之前必须先改解析, 否则正式包的支付回调和分享链接会打到测试服。

8.5 应用登记与产品

  • 确认 app_registry 那行改完后后台能正常切换应用(§2 P0-1)。
  • 产品台账按你的决定不搬,需要在正式后台重新建产品、批次、导入设备 MAC —— 影响见 §12。

9. 阶段 G · 验收清单

逐条跑,每条都给了「失败时先看哪」:

# 验收项 怎么验 失败先看
1 三个容器都在跑且版本对 docker ps 看 ym-a11 / starpivot-console / starpivot-admin 都是 0.2.0 docker logs
2 home 没 panic docker logs ym-a11 | grep -i panic 无输出 词库/ip2region 文件缺失
3 新表都建出来了 §6 的两段 SQL 服务启动顺序
4 统计不再被拒收 docker logs starpivot-console | grep "未在 console 注册" 无输出 P0-1 没改全
5 后台能登录、能切应用 打开 https://console.ymaikj.com admin 的 CONSOLE_BACKEND
6 游客登录入口结论能取到 curl -sX POST https://ym.ymaikj.com/api/home/user_getchannelapps -d '{}' 不返回 code:18 gateway 白名单漏了 user_getchannelapps
7 第三方配置能解密 后台触发一次发邮件/短信 FIELD_ENCRYPT_KEY 两边不一致(报 401/鉴权失败但后台显示配置正常)
8 文件上传 App 或 curl 走 user_getuploadurl 拿预签名再 PUT OSS 配置、scene 白名单、Content-Type 必须用服务端回带的那个
9 MCP 可达 curl -o /dev/null -w "%{http_code}\n" https://mcp.ymaikj.com/ —— 拿到 000 说明证书没签,拿到 openresty 默认页说明反代没建 §8.3 那条 proxy host / MCP_ADDR 还是 localhost
10 MCP 鉴权 登录后 user_getmcptoken 能拿到令牌 MCP_TOKEN_KEY 缺失或两边不同值
11 实名核验 user_idverify 走通 svc_config 的 idverify_* 没搬 / ID_HASH_SALT 缺(缺盐不阻断,只是不写指纹)
12 会议转写回调 起一次转写任务,看阿里回调有没有进来 echomeet_alibackcall 没进白名单(静默)
13 客户端配置下发 user_getappconfig 的 env 非空 业务库 config 表没搬

10. 回滚

cd deploy/app     && ./prod-deploy.sh ym along rollback
cd ../console     && ./prod-deploy.sh rollback along
cd ../admin       && ./prod-deploy.sh rollback along

⚠️ 镜像能回滚,数据库迁移不能。 product.devicetype / chip_vendor.cid 这两列是真删了的, 回到 0.1.4 的镜像后老代码仍会读它们。所以:

  • 回滚必须连同 §3 的库备份一起回退,否则老镜像会报缺列;
  • device_mac 合表刻意保留了旧的 license_* 分表,这一项本身可回退;
  • 若只是某个功能不对劲,优先在后台关掉该功能 / 改配置,不要轻易整体回滚。

11. 红线:绝对不要从测试库搬到正式库的东西

表 为什么
app_service_config 存 DSN / Redis / NATS / COS 地址。搬过去 = 把正式服指向测试库。18 行都要留在正式库自己的值,只改 app_name(P0-1)
app_registry 正式服有自己的注册行(id=1),搬过去会把应用身份搞乱
app_release / channel_app 版本控制、强更、游客显隐、渠道下载地址。测试服的版本号照搬到正式会误触发强更或让入口消失
console_account 后台账号与 bcrypt 密码,各环境独立
stats_global_day / settlement_month 统计与结算数据,搬过去等于伪造正式数据
user / userdevice / useridverify / echomeet_record / payorder / useruselog / userstatistics / memory_* / allhelp_task / chat_summary / product_stat / console_log / admin_resource_log 用户数据。正式库本来就是空的,保持空
config 的 COMPUTE_LEGACY_MIGRATED 迁移完成标记,搬了会让正式服跳过旧额度折算

12. 遗留缺口(按你「业务台账不搬」的决定,这些是明确的后果)

  1. 正式服没有任何产品 —— product=0、production_batch=0、device_mac 表为空。直接后果:
    • 设备绑不上:user_binddevice 对经典蓝牙耳机是「按 MAC 反查授权码」,MAC 不在 device_mac 里 一律 AuthorizeNoCanUse;恒玄那条链路还会因此确权失败直接断开连接并弹「设备连接受限」。
    • OTA 不可用:固件版本挂在产品上(user_getappconfig 的 products),没有产品就永远「已是最新」。
    • 要用起来,得在正式后台依次建:方案商 → 品牌商 → 渠道商 → 产品 → 生产批次 → 导入设备 MAC。 如果正式服近期要接真机,这一步比什么都优先,建议单独排期。
  2. echomeet_template 正式有 1189 行、测试只有 287 行 —— 疑似历史上重复 seed 过。 不影响功能(按 id 取),但后台模板列表会很长。要清理得先确认哪些是 seed、哪些是人工加的,本次不动。
  3. CLUSTER_TAG 两台机同名 —— 目前各自 etcd 独立,不致命;建议本次一并改成 eaimar-prod。 ⚠️ 改了要整体重启,改一半会让同机内五个服务互相发现不到。
  4. echomeet_alibackcall 没有签名校验 —— 只靠阿里下发的 UUID task_id 不可猜。 正式环境建议后续补回调密钥或签名。
  5. 仓库的 gateway.yaml.example 还缺 echomeet_alibackcall —— 下次新环境部署会重复踩。 建议把这行补进模板(本次未改)。
  6. pay / share / web 三个域名仍解析到测试机 8.133.166.29 —— 见 §8.4。本轮不阻塞, 正式发客户端前必须改解析,否则正式包的支付回调、分享链接会打到测试服。
  7. 客户端未发版 —— 老包仍指向 ym-dev.ymaikj.com(测试环境)。正式服上线后 没有任何客户端在连它,验收只能靠 curl / 后台。要真正投入使用需另发一版把 SERVER_URL 切到 ym.ymaikj.com,届时还要配「版本控制 / 游客显隐控制」两页。

13. 实际执行记录(2026-09-11 已落地)

本次按本文档执行完毕,实际情况与计划的出入都记在下面。

已完成

# 动作 结果
1 备份 正式机 /root/backup-020/:console 主库 3.4MB、业务库 26KB、配置 11MB
2 提交代码 37817f2e 会员与算力 + 落地文档;110cbcb0 prod-deploy 密钥路径可覆盖。未推远端
3 构建镜像 没有重新构建,改为把测试机验证过的 dev-latest 打成 0.2.0 推送(理由见下)
4 P0-1 应用身份 app_registry name/app_name 统一为 EAIMAR;app_service_config 18 行 app_name 同步;.env 的 ANALYZE_APP_NAME=EAIMAR
5 P0-3 密钥路径 三个 prod-deploy.sh 的 along 段改为 ${DEPLOY_KEY:-原路径},部署时用 DEPLOY_KEY=~/Documents/keys/loginscre.pem
6 .env 新增 MCP_TOKEN_KEY(64 位随机,与 GATEWAY_TOKEN_KEY 不同)、ID_HASH_SALT(48 位随机)。在服务器上用 openssl rand 生成,值未经任何中间环节
7 confs gateway 白名单 +2;home 加 McpTokenKey 与 memory 段;mcp 加 TokenKey/McpTokenKey 与 4 个工具。改前备份 *.yaml.bak-020,改后用 pyyaml 校验 5 个文件全部解析通过
8 部署 console → app → admin,全部 0.2.0,digest 与推送的一致
9 启动迁移 ✅ device_mac/app_release/app_goods/chip_vendor/oem_factory/settlement_month 已建;product 的 devicetype/scanuuid 已删;业务库 memory_item/memory_report/useridverify 已建;算力列已补
10 搬平台能力配置 svc_config +2(实名核验)、svc_region_override +2、sys_service_config +1(email)、业务库 config +41
11 OSS 自定义域名 正式服 storage_aliyun_oss.domain 原为空串,已补 https://oss.ymaikj.com
12 MCP_ADDR 由 http://localhost:7300 改为 https://mcp.ymaikj.com,已生效(容器内 /sse 返回的 endpoint 就是这个域名)

与计划的三处出入

  1. 镜像没有本机重建,而是给 dev-latest 打标签。 本机 Docker daemon 无响应。 核对过 dev-deploy.sh 与 prod-build.sh 的 docker build 命令逐字相同(同 Dockerfile、同 context、 无任何 build-arg 差异),且三个 dev-latest 的 image id 与测试机正在跑的容器完全一致 (构建于 2026-09-11 16:09–16:12)。所以 0.2.0 与测试服验证过的是同一个 digest, 比本机重建更忠实于「把测试服的东西搬过去」。

  2. config 表导入时撞了主键。 app 升级后启动写入了 COMPUTE_LEGACY_MIGRATED 标记, 自增拿到 id=1,正好撞上测试库第一条配置。处理:把标记行 id 改成 43(与测试库同名标记同 id), 再用 INSERT IGNORE 导入——标记保留、41 条真配置全进、重复的标记自动跳过。 本文档 §7.2 的顺序有个坑:先部署再导 config,就会遇到这个冲突;下次新环境上线, config 表应在 app 首次启动之前导入。

  3. 算力/会员参数(§8.1)没有配。 实测测试库 config 表里也没有这些键,走的是代码默认值 (系数全 1、COMPUTE_GATE 关、开户礼 30 天 VIP + 100 算力)。保持不配 = 与测试服行为一致。 要调就去后台「会员与算力」页。

验收结果

验收项 结果
三容器版本 ✅ 全部 0.2.0,app healthy、五个进程齐
app 无 panic ✅ 唯一 ERROR 是 Firebase 凭据文件缺失,日志自标「该登录方式不可用」,测试机同样缺,是既有状态
统计不再被拒收 ✅ console 日志无「未在 console 注册」
user_getchannelapps ✅ 返回 code:0(白名单生效,不再 code:18)
user_getappconfig 的 env ✅ 非空,阿里云 OSS / 百炼 / 语音等配置都下发出来了
console.ymaikj.com ✅ 200
MCP 服务本身 ✅ 容器内 /sse 正常,BaseURL 已是 https://mcp.ymaikj.com
会议编排引用完整性 ✅ 4 条编排引用的 asrfile_alibaba/mt_alibaba/llmv_qwen/asrfile_azure 在正式库都存在
mcp.ymaikj.com 外网 ❌ https=000,NPM 反代未建(见下)

⚠️ 剩余两件事(需要人工操作)

① stats_global_day 主键缺 channel_id,统计快照会全部写入失败。

console 启动日志明确报了:

WARN console.stat: stats_global_day 复合主键缺列,统计快照将全部写入失败(42P10),
     请执行 docs/migrations/2026-08-08-channel-dimension.sql 的 A4 段

实测该表 0 行、channel_id 列已由 console 启动时补上,只是主键没含它,所以重建主键零风险。 这三条 DDL 被本次会话的权限策略挡住(DROP CONSTRAINT),需手动执行:

ssh -i ~/Documents/keys/loginscre.pem root@47.116.104.181
docker exec postgres psql -U admin -d starpivot_console <<'SQL'
ALTER TABLE stats_global_day DROP CONSTRAINT stats_global_day_pkey;
ALTER TABLE stats_global_day
  ADD CONSTRAINT stats_global_day_pkey PRIMARY KEY (app_id, product_id, channel_id, region, stat_day);
CREATE INDEX IF NOT EXISTS idx_stats_channel_day ON stats_global_day (channel_id, stat_day);
SQL
docker restart starpivot-console     # 重启后那条 WARN 应消失

不做的后果:后台看板恒为 0。正式服目前没有用户、不产生统计,所以不阻塞上线,但接入真实流量前必须做完。

② mcp.ymaikj.com 的 NPM 反代与证书。

DNS 已指向 47.116.104.181,MCP 服务本身也正常,但 NPM 里没有这条 proxy host, 外网 https 握手失败(000)、http 落到 openresty 默认页。在 NPM 后台新建:

  • Domain mcp.ymaikj.com → ym-a11 : 7300
  • 申请 Let's Encrypt 证书
  • 勾上 Websockets Support(MCP 走 SSE 长连接,不勾会被缓冲/断开,表现为「连上了但事件不下来」)

可直接照抄测试机 mcp-dev.ymaikj.com 那条的配置。

还需要在后台补的业务配置(不影响服务运行)

  • 渠道分发 / 版本控制:user_getchannelapps 现在返回 code:0 但 data 为空—— channel_app 里只有一条 app_name='Voitrans-Test' 的测试残留(app_name 对不上 EAIMAR,读不到)。 正式发客户端前要为 EAIMAR 配渠道分发与版本控制,否则游客登录入口的显隐结论拿不到,客户端按「隐藏」处理。 那条 Voitrans-Test 残留没有清理(它读不到、不影响任何功能),确认后可删。
  • 产品台账:product / production_batch / device_mac 仍为空(按本次「业务台账不搬」的决定)。 后果见 §12 第 1 条——设备绑不上、OTA 不可用。要接真机就得先建起来。

附:本文档的事实来源

除标注为「建议」的部分,其余均为 2026-09-11 在两台服务器上实测: docker ps / .env 键集合 / confs/*.yaml 逐字 diff / pg_tables 与 show tables 表清单 / 关键表行数与 app_name 分布 / NPM proxy host 清单。脚本行为来自 deploy/{app,console,admin}/{prod-build,prod-deploy}.sh 与 deploy/registry-profiles.sh。