8.9 KiB
convert-wecom-ids-gk
把第三方应用获取的:
external_userid(服务商 external_userid)open_userid(密文 open_userid)
转换为自建应用/企业内部可用的:
external_userid(企业 external_userid)userid(明文 userid)
并把转换后的数据写入新的 MongoDB collection(原名 + -gk 后缀,如 corp-member-gk)。
安全说明:
- 默认 dry-run:不创建、不写入任何 collection
- 只有
--apply --yes才会写入 - 永远不会更新原 collection(只读原表)
- 写入
*-gk时默认使用 upsert(按_id覆盖更新),因此报错后可直接重跑,一般不需要删表
依赖与运行方式
- Node.js(建议 18+)
- 依赖:
mongodb、axios(项目内已安装)
在目录 c:\code\yk\ykt\ytk-customer-service 下运行:
cd c:\code\yk\ykt\ytk-customer-service
node scripts/convert-wecom-ids-gk.js --help
脚本不读取任何 .env 文件;Mongo/企微参数全部通过命令行参数或环境变量提供。
注:脚本文件为 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 做脱敏)。
典型用法
1) dry-run(推荐先跑)
只扫描统计,不写入任何 *-gk 表:
node scripts/convert-wecom-ids-gk.js `
--mongoUri "mongodb://<user>:<pass>@<host>:27017/admin" `
--dbName corp `
--targetCorpId wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA `
--skipApi
Linux / macOS(bash/zsh):
node scripts/convert-wecom-ids-gk.js \
--mongoUri "mongodb://<user>:<pass>@<host>:27017/admin" \
--dbName corp \
--targetCorpId wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA \
--skipApi
2) 实际写入 *-gk(只新增,不改原表)
PowerShell:
$env:WECOM_SECRET='你的corpsecret'
node scripts/convert-wecom-ids-gk.js `
--apply --yes `
--mongoUri "mongodb://<user>:<pass>@<host>:27017/admin" `
--dbName corp `
--collections wechat-friends `
--targetCorpId wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA `
--sourceAgentId 1000076
Linux / macOS(bash/zsh):
export WECOM_SECRET='你的corpsecret'
node scripts/convert-wecom-ids-gk.js \
--apply --yes \
--mongoUri "mongodb://<user>:<pass>@<host>:27017/admin" \
--dbName corp \
--collections wechat-friends \
--targetCorpId wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA \
--sourceAgentId 1000076
3) 只跑指定 collection(便于测试)
node scripts/convert-wecom-ids-gk.js \
--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 升序,便于稳定分段):
node scripts/convert-wecom-ids-gk.js \
--apply --yes \
--mongoUri "mongodb://<user>:<pass>@<host>:27017/admin" \
--dbName corp \
--collections wechat-friends \
--targetCorpId wpLgjyawAAeRkCPQMp9-z5q-xEzK64nA \
--limitDocs 1000
分段跑下一段(跳过前 1000 条,再跑 1000 条):
node scripts/convert-wecom-ids-gk.js \
--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_taguser/list_id(文档 path=96067):拉取“当前 token 可见的成员 userid 列表”
只做排查、不跑转换:
node scripts/convert-wecom-ids-gk.js \
--preset gk-suite \
--dumpScope --scopeOnly
检查某些 userid 是否在可见范围内:
node scripts/convert-wecom-ids-gk.js \
--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,也不写入 *-gk),可以用:
node scripts/convert-wecom-ids-gk.js \
--tokenMode corpSecret \
--tokenCorpId <corpId> \
--secret "<corpsecret>" \
--sourceAgentId 1000076 \
--openUserIds woXXXX,woYYYY \
--externalUserIds wmXXXX,wmYYYY
tokenMode=suite 也支持直接提供 token 所需参数(无需 Mongo):
node scripts/convert-wecom-ids-gk.js \
--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
- 依赖:MongoDB 的
--tokenMode corpSecret(默认):使用gettoken(仅适用于自建应用/企业内部应用场景)--tokenCorpId <corpId>:用于gettoken的 corpId--secret <corpsecret>:corpsecret--secretEnv <ENV_NAME>:从环境变量读取 corpsecret(推荐)
--sourceAgentId <agentid>:第三方应用的source_agentid- 不传时脚本会尝试从
corpcollection 的auth_info.agent[0].agentid推断(推断不到会报错)
- 不传时脚本会尝试从
写入与安全开关
--apply:执行写入(不传则 dry-run)--yes:--apply时必须显式确认(避免误操作)--insertOnly:仅使用insertMany(不建议;报错重跑容易重复插入或因重复_id报错)--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),用法:
node scripts/convert-wecom-ids-gk.js --listPresets
node scripts/convert-wecom-ids-gk.js --preset gk-suite --collections wechat-friends --limitDocs 200 --apply --yes
建议在 Linux 上把敏感信息放环境变量里:
export GK_MONGO_URI='mongodb://<user>:<pass>@<host>:27017/admin'
export GK_CORP_SECRET='你的corpsecret' # 仅 tokenMode=corpSecret 时需要