36 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 在两台机上实测得到的,不是照着脚本推的:
- 正式服没有任何用户数据 ——
user=0、userdevice=0、echomeet_record=0、payorder空。 所以「用户数据不要部署过去」这条天然满足,不需要任何排除规则、不需要脱敏。 - 正式服代码停在 8-18 的
0.1.4,这三周的能力一个都没有:记忆中心(拾忆)、会员与算力、 身份证实名核验、设备表合表(license_<pid>→device_mac)、预签名直传 OSS、 MCP 令牌与会议纪要检索、发版配置拆表(app_release/channel_app)。 - 正式服有三处地基性错配(§2),不先修的话新能力部上去也是静默失效——不报错、看板恒为 0、 按应用作用域的配置全部退化成全局默认。
- 正式服的 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 个文件、console6 个文件、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 就必须放行, 否则回调进不来、转写退化成客户端轮询,且静默。 ⚠️ 该接口自身没有签名校验,只靠阿里下发的 UUIDtask_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 三个目录都是空的。
所以只剩三步:
- NPM 新建 proxy host:
mcp.ymaikj.com→ym-a11:7300,申请证书, 必须勾上 WebSockets Support —— MCP 走 SSE 长连接,默认配置会被缓冲/断开 .env的MCP_ADDR=https://mcp.ymaikj.com,重建容器(MCP_ADDR是下发给百炼的 SSE BaseURL)- 百炼控制台把 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. 遗留缺口(按你「业务台账不搬」的决定,这些是明确的后果)
- 正式服没有任何产品 ——
product=0、production_batch=0、device_mac表为空。直接后果:- 设备绑不上:
user_binddevice对经典蓝牙耳机是「按 MAC 反查授权码」,MAC 不在device_mac里 一律AuthorizeNoCanUse;恒玄那条链路还会因此确权失败直接断开连接并弹「设备连接受限」。 - OTA 不可用:固件版本挂在产品上(
user_getappconfig的 products),没有产品就永远「已是最新」。 - 要用起来,得在正式后台依次建:方案商 → 品牌商 → 渠道商 → 产品 → 生产批次 → 导入设备 MAC。 如果正式服近期要接真机,这一步比什么都优先,建议单独排期。
- 设备绑不上:
echomeet_template正式有 1189 行、测试只有 287 行 —— 疑似历史上重复 seed 过。 不影响功能(按 id 取),但后台模板列表会很长。要清理得先确认哪些是 seed、哪些是人工加的,本次不动。CLUSTER_TAG两台机同名 —— 目前各自 etcd 独立,不致命;建议本次一并改成eaimar-prod。 ⚠️ 改了要整体重启,改一半会让同机内五个服务互相发现不到。echomeet_alibackcall没有签名校验 —— 只靠阿里下发的 UUIDtask_id不可猜。 正式环境建议后续补回调密钥或签名。- 仓库的
gateway.yaml.example还缺echomeet_alibackcall—— 下次新环境部署会重复踩。 建议把这行补进模板(本次未改)。 pay/share/web三个域名仍解析到测试机 8.133.166.29 —— 见 §8.4。本轮不阻塞, 正式发客户端前必须改解析,否则正式包的支付回调、分享链接会打到测试服。- 客户端未发版 —— 老包仍指向
ym-dev.ymaikj.com(测试环境)。正式服上线后 没有任何客户端在连它,验收只能靠 curl / 后台。要真正投入使用需另发一版把SERVER_URL切到ym.ymaikj.com,届时还要配「版本控制 / 游客显隐控制」两页。
附:本文档的事实来源
除标注为「建议」的部分,其余均为 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。