corp-transfer/convert-wecom-ids-gk.README.md

282 lines
8.7 KiB
Markdown
Raw Normal View History

2026-03-13 17:07:27 +08:00
# convert-wecom-ids-gk
把第三方应用获取的:
- `external_userid`(服务商 external_userid
- `open_userid`(密文 open_userid
转换为自建应用/企业内部可用的:
- `external_userid`(企业 external_userid
- `userid`(明文 userid
并把转换后的数据按 `_id` 回写到原 MongoDB collection。
2026-03-13 17:07:27 +08:00
安全说明:
- 默认 dry-run不写入任何 collection
2026-03-13 17:07:27 +08:00
- 只有 `--apply --yes` 才会写入
- `--apply` 会直接更新原 collection
- 回写时默认使用 **upsert**(按 `_id` 覆盖更新),因此报错后可直接重跑
2026-03-13 17:07:27 +08:00
---
## 依赖与运行方式
- Node.js建议 18+
- 依赖:`mongodb``axios`(项目内已安装)
在目录 `c:\code\yk\gykq\transfer` 下运行:
2026-03-13 17:07:27 +08:00
```powershell
cd c:\code\yk\gykq\transfer
node transfer.js --help
2026-03-13 17:07:27 +08:00
```
脚本支持读取 `.env``.env.<NODE_ENV>``env.<NODE_ENV>`。当前仓库已可直接使用 `.env.gk`
2026-03-13 17:07:27 +08:00
注:脚本文件为 UTF-8 编码;在 Windows PowerShell 里如果直接 `Get-Content` 看到中文乱码,请用 `Get-Content -Encoding UTF8 scripts/convert-wecom-ids-gk.js` 查看。
---
## 企业微信接口(脚本内部调用)
- 获取 `access_token``/cgi-bin/gettoken`
- open_userid -> userid`/cgi-bin/batch/openuserid_to_userid`
- 服务商 external_userid -> 企业 external_userid`/cgi-bin/externalcontact/from_service_external_userid`
若遇到 `errcode=60020 not allow to access from your ip`,需要把运行机器出口 IP 加到企业微信白名单后再执行。
补充:文档里很多地址写成 `.../xxx?access_token=ACCESS_TOKEN`。脚本里使用 `axios``params` 传参,最终效果仍然是拼到 URL query string日志里会以 `fullUrl` 字段打印出来,并对 token 做脱敏)。
---
## 典型用法
如果你已经准备好了 `.env.gk`,可直接运行:
```powershell
node transfer.js --apply --yes
```
执行 `npm run build` 后,`.env.gk` 会被内嵌进 `dist/transfer.js`,因此发布产物是单文件。若你修改了 `.env.gk`,需要重新构建一次。
2026-03-13 17:07:27 +08:00
### 1) dry-run推荐先跑
只扫描统计,不写入任何表:
2026-03-13 17:07:27 +08:00
```powershell
node transfer.js `
2026-03-13 17:07:27 +08:00
--mongoUri "mongodb://<user>:<pass>@<host>:27017/admin" `
--dbName corp `
--targetCorpId wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA `
--skipApi
```
Linux / macOSbash/zsh
```bash
node transfer.js \
2026-03-13 17:07:27 +08:00
--mongoUri "mongodb://<user>:<pass>@<host>:27017/admin" \
--dbName corp \
--targetCorpId wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA \
--skipApi
```
### 2) 实际回写原 collection
2026-03-13 17:07:27 +08:00
PowerShell
```powershell
$env:WECOM_SECRET='你的corpsecret'
node transfer.js `
2026-03-13 17:07:27 +08:00
--apply --yes `
--mongoUri "mongodb://<user>:<pass>@<host>:27017/admin" `
--dbName corp `
--collections wechat-friends `
--targetCorpId wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA `
--sourceAgentId 1000076
```
Linux / macOSbash/zsh
```bash
export WECOM_SECRET='你的corpsecret'
node transfer.js \
2026-03-13 17:07:27 +08:00
--apply --yes \
--mongoUri "mongodb://<user>:<pass>@<host>:27017/admin" \
--dbName corp \
--collections wechat-friends \
--targetCorpId wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA \
--sourceAgentId 1000076
```
### 3) 只跑指定 collection便于测试
```bash
node transfer.js \
2026-03-13 17:07:27 +08:00
--mongoUri "mongodb://<user>:<pass>@<host>:27017/admin" \
--dbName corp \
--collections wechat-friends,corp-member \
--targetCorpId wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA \
--skipApi
```
### 4) wechat 表数据太多:只跑一部分
只跑 `wechat-friends` 前 1000 条(默认按 `_id` 升序,便于稳定分段):
```bash
node transfer.js \
2026-03-13 17:07:27 +08:00
--apply --yes \
--mongoUri "mongodb://<user>:<pass>@<host>:27017/admin" \
--dbName corp \
--collections wechat-friends \
--targetCorpId wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA \
--limitDocs 1000
```
分段跑下一段(跳过前 1000 条,再跑 1000 条):
```bash
node transfer.js \
2026-03-13 17:07:27 +08:00
--apply --yes \
--mongoUri "mongodb://<user>:<pass>@<host>:27017/admin" \
--dbName corp \
--collections wechat-friends \
--targetCorpId wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA \
--skipDocs 1000 \
--limitDocs 1000
```
---
## 可见范围/成员列表排查(推荐)
当你发现 `open_userid -> userid` 大面积 `invalid_open_userid_list` 时,最常见原因不是“参数写错”,而是:当前 `access_token` 所代表的应用,在通讯录侧可见的成员范围很小(可见范围/权限交集)。
脚本提供 `--dumpScope`,会调用:
- `agent/get`:查看应用的 allow_user/allow_party/allow_tag
- `user/list_id`(文档 path=96067拉取“当前 token 可见的成员 userid 列表”
只做排查、不跑转换:
```bash
node transfer.js \
2026-03-13 17:07:27 +08:00
--preset gk-suite \
--dumpScope --scopeOnly
```
检查某些 userid 是否在可见范围内:
```bash
node transfer.js \
2026-03-13 17:07:27 +08:00
--preset gk-suite \
--dumpScope --scopeOnly \
--checkUserIds 23090101,23090102
```
## 参数说明
### MongoDB任选一种方式
- `--mongoUri "<mongodb uri>"`(或别名 `--mongoUrl "<mongodb uri>"`
- 或拆分参数(也支持对应环境变量兜底):
- `--mongoHost`(或 `MONGO_HOST` / `CONFIG_DB_HOST`
- `--mongoPort`(或 `MONGO_PORT` / `CONFIG_DB_PORT`,默认 `27017`
- `--mongoUser`(或 `MONGO_USER` / `CONFIG_DB_USERNAME`
- `--mongoPass`(或 `MONGO_PASS` / `CONFIG_DB_PASSWORD`
- `--mongoAuthDb`(或 `MONGO_AUTH_DB`,默认 `admin`
### 必填/常用
- `--dbName <db>`:只处理单个数据库(例如 `corp`;注意这是“数据库名”,不是 collection 名)
- `--targetCorpId <corpId>`:目标机构 corpId
- `--collections a,b,c`:只处理指定 collection可选例如 `corp-member,wechat-friends`;不传则扫描该库所有 collection
### 指定 ID 转换(不跑数据库)
如果你只想把指定的 `open_userid`/`external_userid` 转换出来用于验证(不扫描 Mongo也不写库可以用
2026-03-13 17:07:27 +08:00
```bash
node transfer.js \
2026-03-13 17:07:27 +08:00
--tokenMode corpSecret \
--tokenCorpId <corpId> \
--secret "<corpsecret>" \
--sourceAgentId 1000076 \
--openUserIds woXXXX,woYYYY \
--externalUserIds wmXXXX,wmYYYY
```
tokenMode=suite 也支持直接提供 token 所需参数(无需 Mongo
```bash
node transfer.js \
2026-03-13 17:07:27 +08:00
--tokenMode suite \
--suiteAccessToken "<suite_access_token>" \
--permanentCode "<permanent_code>" \
--targetCorpId <auth_corpid> \
--sourceAgentId 1000076 \
--openUserIds woXXXX
```
### 只跑部分数据(单表分段)
- `--limitDocs <n>`:每个 collection 最多处理 n 条
- `--skipDocs <n>`:每个 collection 跳过前 n 条
- `--noSortById`:不按 `_id` 排序(默认按 `_id` 升序,分段更稳定)
### 企微转换相关(`--skipApi` 关闭时需要)
- access_token 获取方式2 选 1
- `--tokenMode suite`(推荐):使用第三方应用的 suite_token + 目标机构 permanent_code 获取该机构的 corp access_token
- 依赖MongoDB 的 `corp.weComToken(type=suiteToken).suite_token`、以及 `corp.corp.permanent_code`
- 无需传 `--tokenCorpId/--secret`
- `--tokenMode corpSecret`(默认):使用 `gettoken`(仅适用于自建应用/企业内部应用场景)
- `--tokenCorpId <corpId>`:用于 `gettoken` 的 corpId
- `--secret <corpsecret>`corpsecret
- `--secretEnv <ENV_NAME>`:从环境变量读取 corpsecret推荐
- `--sourceAgentId <agentid>`:第三方应用的 `source_agentid`
- 不传时脚本会尝试从 `corp` collection 的 `auth_info.agent[0].agentid` 推断(推断不到会报错)
### 写入与安全开关
- `--apply`:执行写入(不传则 dry-run
- `--yes``--apply` 时必须显式确认(避免误操作)
- `--insertOnly`:已不再支持;当前 apply 模式固定按 `_id` upsert 回写原表
2026-03-13 17:07:27 +08:00
- `--skipApi`:只扫描统计,不调企业微信接口(仅 dry-run 可用)
### 扫描/性能参数(可选)
- `--corpIdFields corpId,corpid,corp_id`:用于判断该 collection 是否属于目标 corp
- `--batchDocs 200`Mongo 游标批大小
- `--batchOpenIds 100``open_userid` 批量转换每次请求数量
- `--concurrencyExternal 8`external_userid 单条转换并发数
- `--aggressive`:更激进地识别字段/字符串(可能增加 API 调用量)
### 企微接口日志
- 默认会输出每一次企微接口调用的完整响应(成功/失败都会输出)
- 如需关闭:`--noWecomDebug`
---
## 预设(减少命令行参数)
脚本内置了少量预设(不包含任何明文账号/密码/secret用法
```bash
node transfer.js --listPresets
node transfer.js --preset gk-suite --collections wechat-friends --limitDocs 200 --apply --yes
2026-03-13 17:07:27 +08:00
```
建议在 Linux 上把敏感信息放环境变量里:
```bash
export GK_MONGO_URI='mongodb://<user>:<pass>@<host>:27017/admin'
export GK_CORP_SECRET='你的corpsecret' # 仅 tokenMode=corpSecret 时需要
```