corp-transfer/convert-wecom-ids-gk.README.md
2026-03-13 17:07:27 +08:00

8.9 KiB
Raw Blame History

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+
  • 依赖:mongodbaxios(项目内已安装)

在目录 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。脚本里使用 axiosparams 传参,最终效果仍然是拼到 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 / macOSbash/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 / macOSbash/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_tag
  • user/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
    • --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:仅使用 insertMany(不建议;报错重跑容易重复插入或因重复 _id 报错)
  • --skipApi:只扫描统计,不调企业微信接口(仅 dry-run 可用)

扫描/性能参数(可选)

  • --corpIdFields corpId,corpid,corp_id:用于判断该 collection 是否属于目标 corp
  • --batchDocs 200Mongo 游标批大小
  • --batchOpenIds 100open_userid 批量转换每次请求数量
  • --concurrencyExternal 8external_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 时需要