2026-04-03 10:26:45 +08:00
|
|
|
|
# 企业微信 ID 转换与数据迁移操作指南
|
|
|
|
|
|
|
|
|
|
|
|
本文档介绍如何编译打包、运行企业微信数据迁移任务(`transfer.js` 等脚本),以及执行完毕后的数据验证方式。
|
|
|
|
|
|
|
|
|
|
|
|
## 1. 整体流程
|
|
|
|
|
|
迁移主要包含三个步骤:
|
|
|
|
|
|
1. **构建打包(可选但推荐)**:打包脚本,将配置信息直接注入到单产物中,方便分发到不同机器(如接收端机器),不依赖外部 `node_modules`。
|
|
|
|
|
|
2. **测试评估(Dry-run)**:获取 API 数据,扫描相关表进行日志打印统计,但不做实际写入。
|
|
|
|
|
|
3. **真实修改(Apply)**:将转换后的用户 ID 写回原有的 MongoDB 集合中。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 2. 构建与打包准备 (Build)
|
|
|
|
|
|
|
|
|
|
|
|
如果需要在生产机上直接运行而不需要安装库依赖,可使用已配置好的 `esbuild` 进行单文件打包。
|
|
|
|
|
|
|
|
|
|
|
|
1. 进入项目目录并安装依赖(如尚未安装):
|
|
|
|
|
|
```bash
|
|
|
|
|
|
cd c:\code\yk\gykq\transfer
|
|
|
|
|
|
npm install
|
|
|
|
|
|
```
|
|
|
|
|
|
2. 准备环境变量文件(如 `.env.gk`)。打包脚本会自动将其读取并注入到构建结果中。
|
|
|
|
|
|
3. 执行打包命令:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
npm run build
|
2026-04-08 17:23:54 +08:00
|
|
|
|
注意:下面这些命令不要连同终端提示符一起复制,比如不要把 `[root@PAT-MAN-DATA ~]#`、`>` 这种字符带进去。`>` 在 bash 里是续行提示符,复制进去后很容易变成“命令还没结束”的状态,看起来就像“完全没运行”。
|
2026-04-03 10:26:45 +08:00
|
|
|
|
```
|
|
|
|
|
|
*产物将生成在 `dist/` 目录下(如 `dist/transfer.js`、`dist/corpid.js`),你可以把这个单文件发给对方,对端机器只需安装了 Node.js 就能直接跑。*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 3. 运行指南 (Run)
|
|
|
|
|
|
|
|
|
|
|
|
以迁移企业微信 `open_userid` 及 `external_userid` 为例,核心使用 `transfer.js`。
|
|
|
|
|
|
|
|
|
|
|
|
如果已经使用内置 env(例如 `.env.gk`,或打包后的 `dist/transfer.js`),通常不需要再手动传 `--mongoUri`、`--dbName`、`--targetCorpId`、`--sourceAgentId` 这类基础参数;脚本会直接读取内置配置。只有临时覆盖某个值时,才需要显式加参数。
|
|
|
|
|
|
|
2026-04-08 17:23:54 +08:00
|
|
|
|
print("old corpId count:", col.countDocuments({ $or: [ { corpId: oldCorpId }, { corpid: oldCorpId }, { corp_id: oldCorpId } ] }));
|
|
|
|
|
|
|
|
|
|
|
|
- MongoDB:`mongodb://root:ykt&GK87892@10.16.146.81:27017/admin`
|
|
|
|
|
|
- 数据库:`corp`
|
|
|
|
|
|
- 默认 collection:`corp-member`
|
|
|
|
|
|
- 目标 corpId:`wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA`
|
|
|
|
|
|
- token 模式:`corpSecret`
|
|
|
|
|
|
- tokenCorpId:`wwa54dfba0b5441ef1`
|
|
|
|
|
|
- sourceAgentId:`1000076`
|
|
|
|
|
|
- corpSecret:已在 `.env.gk` 中配置,不在文档里展开明文
|
|
|
|
|
|
|
|
|
|
|
|
print("new corpId count:", col.countDocuments({ $or: [ { corpId: newCorpId }, { corpid: newCorpId }, { corp_id: newCorpId } ] }));
|
|
|
|
|
|
printjson(col.findOne({ $or: [ { corpId: newCorpId }, { corpid: newCorpId }, { corp_id: newCorpId } ] }, { projection: { corpId: 1, corpid: 1, corp_id: 1, internal_agentid: 1 } }));
|
2026-04-03 10:26:45 +08:00
|
|
|
|
|
|
|
|
|
|
**内置 env 直接执行:**
|
|
|
|
|
|
```bash
|
|
|
|
|
|
node transfer.js --skipApi
|
|
|
|
|
|
```
|
|
|
|
|
|
**或者执行打包后的产物:**
|
|
|
|
|
|
```bash
|
2026-04-08 17:23:54 +08:00
|
|
|
|
printjson(col.findOne({}, { projection: { open_userid: 1, userid: 1, external_userid: 1, corpId: 1, corpid: 1, corp_id: 1 } }));
|
2026-04-03 10:26:45 +08:00
|
|
|
|
```
|
2026-04-08 17:23:54 +08:00
|
|
|
|
如果你想显式写出来当前默认值,也可以这样执行:
|
2026-04-03 10:26:45 +08:00
|
|
|
|
|
2026-04-08 17:23:54 +08:00
|
|
|
|
```bash
|
|
|
|
|
|
node transfer.js --mongoUri "mongodb://root:ykt&GK87892@10.16.146.81:27017/admin" --dbName corp --targetCorpId wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA --skipApi
|
|
|
|
|
|
```
|
|
|
|
|
|
*注:可通过日志查看预计会有多少条数据被改变、读取的数据形态是否符合预期。*
|
|
|
|
|
|
print("open_userid-like count:", col.countDocuments({ open_userid: /^wo/ }));
|
|
|
|
|
|
print("external_userid-like count:", col.countDocuments({ external_userid: /^wm/ }));
|
2026-04-03 10:26:45 +08:00
|
|
|
|
在确认空跑日志报错和逻辑都没有问题后,带上 `--apply --yes` 标志位做真实写入。
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 需保证服务器外网出口 IP 已经在企业微信开发平台的白名单中
|
|
|
|
|
|
node dist/transfer.js --apply --yes
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
如果只是想临时覆盖某个默认值,可以继续追加参数,例如:
|
|
|
|
|
|
|
2026-04-08 17:23:54 +08:00
|
|
|
|
print("old corpId count:", col.countDocuments({ $or: [ { corpId: oldCorpId }, { corpid: oldCorpId }, { corp_id: oldCorpId } ] }));
|
|
|
|
|
|
print("new corpId count:", col.countDocuments({ $or: [ { corpId: newCorpId }, { corpid: newCorpId }, { corp_id: newCorpId } ] }));
|
|
|
|
|
|
printjson(col.findOne({}, { projection: { corpId: 1, corpid: 1, corp_id: 1, internal_agentid: 1 } }));
|
2026-04-03 10:26:45 +08:00
|
|
|
|
|
|
|
|
|
|
> **提示**:脚本设计为使用 MongoDB 的 `upsert` 或根据 `_id` 覆盖写入。如果在中途网络断开或因接口限制(如并发 / token 过期)导致报错退出,直接原样重启脚本即可,不会造成脏数据重复写入。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 4. 如何验证 (Verify)
|
|
|
|
|
|
|
|
|
|
|
|
转换脚本执行完毕后,可通过以下三种方式进行验证:
|
|
|
|
|
|
|
|
|
|
|
|
### 4.1 日志验证
|
|
|
|
|
|
检查终端输出的最终 Summary 日志。
|
|
|
|
|
|
|
|
|
|
|
|
### 4.2 数据库数据验证
|
|
|
|
|
|
使用 MongoDB 客户端(如 MongoDB Compass、Navicat 或 mosh)连接数据库:
|
|
|
|
|
|
1. 打开被修改的数据表(例如 `wechat-friends` 或具体对应业务的 collection)。
|
|
|
|
|
|
2. 核对被转换的字段(如 `open_userid` 是否已变更为了 `userid`,服务商的 `external_userid` 是否变更为企业的 `external_userid`)。
|
|
|
|
|
|
3. 检查原数据文档的其他字段是否存在丢失。得益于 `_id` 覆盖更新机制,确保只变更多出的或改写的字段。
|
|
|
|
|
|
|
|
|
|
|
|
### 4.3 业务功能验证
|
|
|
|
|
|
登录实际业务系统或者企业微信应用端:
|
|
|
|
|
|
1. **身份鉴权测试**:尝试调用依赖当前企微系统的登录模块或回调模块,验证基于新 `userid` 是否能正常查到员工权限和身份。
|
|
|
|
|
|
2. **外部联系人服务测试**:给发生转换的用户发送测试企微消息或打标签,确认企业微信后台或第三方系统能由转换后的 `external_userid` 成功匹配到对应客户,不报“不是企业客户”之类的错误。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 5. 单独替换 corpId
|
|
|
|
|
|
|
|
|
|
|
|
### 5.1 默认运行方式
|
|
|
|
|
|
|
|
|
|
|
|
`corpid.js` 已经会默认走 `.env.gk`,所以如果环境变量已准备好,直接跑就行:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
node corpid.js
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
实际写入:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
node corpid.js --apply --yes
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-08 17:23:54 +08:00
|
|
|
|
如果你想把当前默认参数展开写出来,等价于:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
node corpid.js --sourceCorpId wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA --newCorpId wwa54dfba0b5441ef1 --dbName corp --collections corp-member --apply --yes
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 5.1.1 当前 `.env.gk` 默认值
|
|
|
|
|
|
|
|
|
|
|
|
当前仓库内置的 `.env.gk` 已经把常用运行参数写好了,文档可以直接按这个默认值理解:
|
|
|
|
|
|
|
|
|
|
|
|
- `MONGO_URI = mongodb://root:ykt&GK87892@10.16.146.81:27017/admin`
|
|
|
|
|
|
- `DB_NAME = corp`
|
|
|
|
|
|
- `COLLECTIONS = corp-member`
|
|
|
|
|
|
- `TARGET_CORP_ID = wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA`
|
|
|
|
|
|
- `TOKEN_MODE = corpSecret`
|
|
|
|
|
|
- `TOKEN_CORP_ID = wwa54dfba0b5441ef1`
|
|
|
|
|
|
- `SOURCE_AGENT_ID = 1000076`
|
|
|
|
|
|
- `CORP_SECRET = 已配置,文档里不展开明文`
|
|
|
|
|
|
|
2026-04-03 10:26:45 +08:00
|
|
|
|
### 5.2 常见定向运行
|
|
|
|
|
|
|
|
|
|
|
|
只处理某个数据库:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
node corpid.js --dbName corp
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
只处理指定 collection:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
node corpid.js --dbName corp --collections corp-member,wechat-friends
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
显式指定旧 corpId 和新 corpId:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
node corpid.js --sourceCorpId <旧corpId> --newCorpId <新corpId>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-08 17:23:54 +08:00
|
|
|
|
如果直接按当前仓库默认值写,可以用:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
node corpid.js --sourceCorpId wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA --newCorpId wwa54dfba0b5441ef1 --dbName corp --collections corp-member
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-03 10:26:45 +08:00
|
|
|
|
补充说明:
|
|
|
|
|
|
|
|
|
|
|
|
- 默认旧 corpId 从 `TARGET_CORP_ID` 取
|
|
|
|
|
|
- 默认新 corpId 从 `TOKEN_CORP_ID` 取
|
|
|
|
|
|
- `corp` collection 里会额外维护 `internal_agentid`
|
|
|
|
|
|
|
|
|
|
|
|
### 5.3 重复字段和重复执行
|
|
|
|
|
|
|
|
|
|
|
|
这个脚本的“重复”有两层含义:
|
|
|
|
|
|
|
|
|
|
|
|
- 同一文档里 `corpId`、`corpid`、`corp_id` 可能同时出现,脚本会全部替换
|
|
|
|
|
|
- 你重复执行同一命令,dry-run 不会落库;apply 模式按原 `_id` 回写,重复跑不会造重复文档
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 6. 数据库迁移
|
|
|
|
|
|
|
|
|
|
|
|
### 6.1 运行顺序
|
|
|
|
|
|
|
|
|
|
|
|
迁移必须先起 receiver,再跑 sender。
|
|
|
|
|
|
|
|
|
|
|
|
1. 在目标环境启动 receiver
|
|
|
|
|
|
2. 在源环境启动 sender,把数据推过去
|
|
|
|
|
|
|
|
|
|
|
|
### 6.2 启动 receiver
|
|
|
|
|
|
|
|
|
|
|
|
receiver 默认监听 8081,默认参数已经写在脚本里。内网/目标机一般直接运行即可:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
node migrate-corpid-receiver.js
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
如果你要改端口或目标 MongoDB,可以覆盖参数:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
node migrate-corpid-receiver.js --port 8081 --targetMongoUri "mongodb://<user>:<pass>@<host>:27017/admin" --token "<token>"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-08 17:23:54 +08:00
|
|
|
|
按当前仓库默认值直接启动 receiver 的完整命令是:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
node migrate-corpid-receiver.js --port 8081 --targetMongoUri "mongodb://root:ykt%26GK87892@10.16.146.81:27017/admin" --token "ca764511bdace06c64f22bb97dd19911bcc4f0c9a07b2211cbbc1d43e9271657"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-03 10:26:45 +08:00
|
|
|
|
### 6.3 启动 sender
|
|
|
|
|
|
|
|
|
|
|
|
sender 默认也带有内置配置,常规情况下直接运行即可:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
node migrate-corpid-sender.js
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
如果要覆盖源库、receiver 地址或 token:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
node migrate-corpid-sender.js --sourceMongoUri "mongodb://<user>:<pass>@<host>:27017/admin" --receiverUrl "http://127.0.0.1:8081" --token "<token>" --corpId "<corpId>"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-08 17:23:54 +08:00
|
|
|
|
按当前脚本内置默认值直接启动 sender 的完整命令是:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
node migrate-corpid-sender.js --sourceMongoUri "mongodb://root:PRO%26youcan2025233@101.35.91.238:27017/admin" --receiverUrl "https://crm.gykqyy.com/jcpt" --token "ca764511bdace06c64f22bb97dd19911bcc4f0c9a07b2211cbbc1d43e9271657" --corpId "wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-03 10:26:45 +08:00
|
|
|
|
### 6.4 迁移范围和重复执行
|
|
|
|
|
|
|
|
|
|
|
|
sender 会扫描源库的业务数据库和 collection,默认过滤 `admin`、`config`、`local` 以外的范围。它会:
|
|
|
|
|
|
|
|
|
|
|
|
- 先把 collection 的索引发给 receiver
|
|
|
|
|
|
- 再按批发送文档
|
|
|
|
|
|
- receiver 用 `_id` 做 upsert 写入
|
|
|
|
|
|
|
|
|
|
|
|
迁移脚本适合重跑:
|
|
|
|
|
|
|
|
|
|
|
|
- 索引有去重缓存
|
|
|
|
|
|
- 文档按 `_id` 覆盖写入
|
|
|
|
|
|
- 中途失败后可以重新执行 sender
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 7. 怎么验证
|
|
|
|
|
|
|
|
|
|
|
|
### 7.1 先看日志
|
|
|
|
|
|
|
|
|
|
|
|
每个脚本结束时都应该看最后的 summary:
|
|
|
|
|
|
|
|
|
|
|
|
- `transfer.js`:看扫描数、命中文档数、写入数、invalid 数
|
|
|
|
|
|
- `corpid.js`:看扫描的数据库数、collection 数、corpId 替换次数、`internal_agentid` 更新次数
|
|
|
|
|
|
- `migrate-corpid-sender.js`:看迁移完成 summary,确认 migratedDocs 数量正常
|
|
|
|
|
|
|
|
|
|
|
|
### 7.2 mongosh 验证命令
|
|
|
|
|
|
|
|
|
|
|
|
下面给的是可以直接复制的 `mongosh` 命令。把 `<MONGO_URI>`、`<DB_NAME>`、`<COLLECTION>`、`<OLD_CORP_ID>`、`<NEW_CORP_ID>` 替换成你的真实值即可。
|
|
|
|
|
|
|
2026-04-08 17:23:54 +08:00
|
|
|
|
如果你直接按当前仓库默认值验证,默认就是:
|
|
|
|
|
|
|
|
|
|
|
|
- `MONGO_URI = mongodb://root:ykt&GK87892@10.16.146.81:27017/admin`
|
|
|
|
|
|
- `DB_NAME = corp`
|
|
|
|
|
|
- `COLLECTION = corp-member`
|
|
|
|
|
|
- `TARGET_CORP_ID = wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA`
|
|
|
|
|
|
- `TOKEN_CORP_ID = wwa54dfba0b5441ef1`
|
|
|
|
|
|
|
2026-04-03 10:26:45 +08:00
|
|
|
|
#### 验证 corpId 替换
|
|
|
|
|
|
|
|
|
|
|
|
先看旧 corpId 还剩多少:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
mongosh "<MONGO_URI>" --quiet --eval '
|
|
|
|
|
|
const dbx = db.getSiblingDB("<DB_NAME>");
|
|
|
|
|
|
const col = dbx.getCollection("<COLLECTION>");
|
|
|
|
|
|
const oldCorpId = "<OLD_CORP_ID>";
|
|
|
|
|
|
print("old corpId count:", col.countDocuments({ $or: [ { corpId: oldCorpId }, { corpid: oldCorpId }, { corp_id: oldCorpId } ] }));
|
|
|
|
|
|
'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
再看新 corpId 命中多少:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
mongosh "<MONGO_URI>" --quiet --eval '
|
|
|
|
|
|
const dbx = db.getSiblingDB("<DB_NAME>");
|
|
|
|
|
|
const col = dbx.getCollection("<COLLECTION>");
|
|
|
|
|
|
const newCorpId = "<NEW_CORP_ID>";
|
|
|
|
|
|
print("new corpId count:", col.countDocuments({ $or: [ { corpId: newCorpId }, { corpid: newCorpId }, { corp_id: newCorpId } ] }));
|
|
|
|
|
|
printjson(col.findOne({ $or: [ { corpId: newCorpId }, { corpid: newCorpId }, { corp_id: newCorpId } ] }, { projection: { corpId: 1, corpid: 1, corp_id: 1, internal_agentid: 1 } }));
|
|
|
|
|
|
'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-08 17:23:54 +08:00
|
|
|
|
如果你想直接按当前默认值验证 `corp-member`,可以直接跑:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
mongosh "mongodb://root:ykt&GK87892@10.16.146.81:27017/admin" --quiet --eval '
|
|
|
|
|
|
const dbx = db.getSiblingDB("corp");
|
|
|
|
|
|
const col = dbx.getCollection("corp-member");
|
|
|
|
|
|
const oldCorpId = "wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA";
|
|
|
|
|
|
const newCorpId = "wwa54dfba0b5441ef1";
|
|
|
|
|
|
print("old corpId count:", col.countDocuments({ $or: [ { corpId: oldCorpId }, { corpid: oldCorpId }, { corp_id: oldCorpId } ] }));
|
|
|
|
|
|
print("new corpId count:", col.countDocuments({ $or: [ { corpId: newCorpId }, { corpid: newCorpId }, { corp_id: newCorpId } ] }));
|
|
|
|
|
|
printjson(col.findOne({}, { projection: { corpId: 1, corpid: 1, corp_id: 1, internal_agentid: 1 } }));
|
|
|
|
|
|
'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-03 10:26:45 +08:00
|
|
|
|
如果你想检查整个数据库里是否还有旧 corpId,可以在 mongosh 里循环所有 collection:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
mongosh "<MONGO_URI>" --quiet --eval '
|
|
|
|
|
|
const dbx = db.getSiblingDB("<DB_NAME>");
|
|
|
|
|
|
const oldCorpId = "<OLD_CORP_ID>";
|
|
|
|
|
|
for (const name of dbx.getCollectionNames()) {
|
|
|
|
|
|
const col = dbx.getCollection(name);
|
|
|
|
|
|
const count = col.countDocuments({ $or: [ { corpId: oldCorpId }, { corpid: oldCorpId }, { corp_id: oldCorpId } ] });
|
|
|
|
|
|
if (count) print(name + ": " + count);
|
|
|
|
|
|
}
|
|
|
|
|
|
'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 验证企微 ID 转换
|
|
|
|
|
|
|
|
|
|
|
|
如果你知道要查的 collection,可以先抽样看一条文档:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
mongosh "<MONGO_URI>" --quiet --eval '
|
|
|
|
|
|
const dbx = db.getSiblingDB("<DB_NAME>");
|
|
|
|
|
|
const col = dbx.getCollection("<COLLECTION>");
|
|
|
|
|
|
printjson(col.findOne({}, { projection: { open_userid: 1, userid: 1, external_userid: 1, corpId: 1, corpid: 1, corp_id: 1 } }));
|
|
|
|
|
|
'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
如果你想确认旧格式值是否还残留,可以针对你已知字段做计数。比如常见的 open_userid / external_userid 形式:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
mongosh "<MONGO_URI>" --quiet --eval '
|
|
|
|
|
|
const dbx = db.getSiblingDB("<DB_NAME>");
|
|
|
|
|
|
const col = dbx.getCollection("<COLLECTION>");
|
|
|
|
|
|
print("open_userid-like count:", col.countDocuments({ open_userid: /^wo/ }));
|
|
|
|
|
|
print("external_userid-like count:", col.countDocuments({ external_userid: /^wm/ }));
|
|
|
|
|
|
'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-08 17:23:54 +08:00
|
|
|
|
如果你要按当前默认库直接看一眼数据,命令可以写成:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
mongosh "mongodb://root:ykt&GK87892@10.16.146.81:27017/admin" --quiet --eval '
|
|
|
|
|
|
const dbx = db.getSiblingDB("corp");
|
|
|
|
|
|
const col = dbx.getCollection("corp-member");
|
|
|
|
|
|
printjson(col.findOne({}, { projection: { open_userid: 1, userid: 1, external_userid: 1, corpId: 1, corpid: 1, corp_id: 1 } }));
|
|
|
|
|
|
print("open_userid-like count:", col.countDocuments({ open_userid: /^wo/ }));
|
|
|
|
|
|
print("external_userid-like count:", col.countDocuments({ external_userid: /^wm/ }));
|
|
|
|
|
|
'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-03 10:26:45 +08:00
|
|
|
|
如果你的数据字段不是顶层字段,而是嵌套在对象里,那么更实用的办法是:
|
|
|
|
|
|
|
|
|
|
|
|
1. 先从日志里拿到处理过的 collection 名
|
|
|
|
|
|
2. 用 `findOne` 抽样看字段
|
|
|
|
|
|
3. 再针对具体字段写 `countDocuments` 或 `find` 验证
|
|
|
|
|
|
|
|
|
|
|
|
#### 验证迁移前后数据量
|
|
|
|
|
|
|
|
|
|
|
|
迁移时最直接的验证方式,是在源库和目标库分别看同一个 collection 的数量是否一致。
|
|
|
|
|
|
|
|
|
|
|
|
源库:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
mongosh "<SOURCE_MONGO_URI>" --quiet --eval '
|
|
|
|
|
|
const dbx = db.getSiblingDB("<DB_NAME>");
|
|
|
|
|
|
print("source count:", dbx.getCollection("<COLLECTION>").estimatedDocumentCount());
|
|
|
|
|
|
'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
目标库:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
mongosh "<TARGET_MONGO_URI>" --quiet --eval '
|
|
|
|
|
|
const dbx = db.getSiblingDB("<DB_NAME>");
|
|
|
|
|
|
print("target count:", dbx.getCollection("<COLLECTION>").estimatedDocumentCount());
|
|
|
|
|
|
'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-08 17:23:54 +08:00
|
|
|
|
如果你是迁移完直接核对当前默认环境,可以分别跑:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
mongosh "mongodb://root:ykt&GK87892@10.16.146.81:27017/admin" --quiet --eval '
|
|
|
|
|
|
const dbx = db.getSiblingDB("corp");
|
|
|
|
|
|
print("corp-member count:", dbx.getCollection("corp-member").estimatedDocumentCount());
|
|
|
|
|
|
'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
mongosh "mongodb://root:PRO%26youcan2025233@101.35.91.238:27017/admin" --quiet --eval '
|
|
|
|
|
|
const dbx = db.getSiblingDB("admin");
|
|
|
|
|
|
print("admin count:", dbx.getCollectionNames().length);
|
|
|
|
|
|
'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-03 10:26:45 +08:00
|
|
|
|
如果要更严谨一点,可以再比对同一批 `_id` 是否能查到:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
mongosh "<TARGET_MONGO_URI>" --quiet --eval '
|
|
|
|
|
|
const dbx = db.getSiblingDB("<DB_NAME>");
|
|
|
|
|
|
const col = dbx.getCollection("<COLLECTION>");
|
|
|
|
|
|
printjson(col.find({}, { projection: { _id: 1 } }).limit(5).toArray());
|
|
|
|
|
|
'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 7.3 验证 receiver 是否在工作
|
|
|
|
|
|
|
|
|
|
|
|
这个不是 mongosh 命令,但很适合一起检查:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
Invoke-RestMethod http://127.0.0.1:8081/health
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
如果返回 `success: true`,说明 receiver 已经在监听。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 8. 推荐执行顺序
|
|
|
|
|
|
|
|
|
|
|
|
最稳妥的顺序是:
|
|
|
|
|
|
|
|
|
|
|
|
1. 先跑 `transfer.js --skipApi` 做 dry-run
|
|
|
|
|
|
2. 用 `mongosh` 抽样看数据形态
|
|
|
|
|
|
3. 再跑 `transfer.js --apply --yes`
|
|
|
|
|
|
4. 跑 `corpid.js` 做 corpId 替换
|
|
|
|
|
|
5. 最后做 sender / receiver 迁移
|
|
|
|
|
|
|
|
|
|
|
|
如果只是要迁移其中一类数据,就只跑对应脚本,不必全跑。
|