@oj-module/cli 0.2.49 → 0.2.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/devkit/README.md +8 -6
  2. package/devkit/SKILL.md +32 -12
  3. package/devkit/manual.md +6 -3
  4. package/devkit/wecom-daikaifa.md +95 -1
  5. package/devkit/wecom-internal-app.md +643 -0
  6. package/package.json +2 -2
  7. package/shell-dist/assets/runtime.js +2 -2
  8. package/shell-dist/index.html +2 -2
  9. package/shell-dist/versions.json +3 -3
  10. package/src/init.ts +5 -4
  11. package/templates/config.yaml +28 -6
  12. package/templates/openapi.json +174 -2
  13. package/templates/src/auth/api/_shared/wecom/admin.ts +87 -7
  14. package/templates/src/auth/api/_shared/wecom/config.ts +256 -46
  15. package/templates/src/auth/api/_shared/wecom/contacts.ts +7 -1
  16. package/templates/src/auth/api/_shared/wecom/events.ts +25 -6
  17. package/templates/src/auth/api/_shared/wecom/login.ts +21 -13
  18. package/templates/src/auth/api/_shared/wecom/store.ts +61 -12
  19. package/templates/src/auth/api/_shared/wecom/sync.ts +11 -1
  20. package/templates/src/auth/api/migrations/0003__credential_kind.mysql.sql +3 -0
  21. package/templates/src/auth/api/migrations/0003__credential_kind.pg.sql +3 -0
  22. package/templates/src/auth/api/migrations/0003__credential_kind.sql +9 -0
  23. package/templates/src/auth/api/schema.yaml +2 -0
  24. package/templates/src/auth/api/wecom/admin/api.ts +112 -11
  25. package/templates/src/auth/api/wecom/api.ts +102 -48
  26. package/templates/src/auth/api/wecom/sync/api.ts +21 -5
  27. package/templates/src/auth/web/__client/api.schemas.ts +22 -1
  28. package/templates/src/auth/web/__client/api.ts +47 -1
  29. package/templates/src/system/web/locales/en-US.json +24 -1
  30. package/templates/src/system/web/locales/zh-CN.json +24 -1
  31. package/templates/src/system/web/pages/departments/sync-bar.tsx +32 -7
  32. package/templates/src/system/web/pages/wecom/index.tsx +164 -46
  33. package/templates/tests/wecom-admin.spec.ts +7 -7
  34. package/templates/tests/wecom-connector.spec.ts +7 -6
  35. package/templates/tests/wecom-corp.spec.ts +523 -0
  36. package/templates/tests/wecom-endpoints.spec.ts +10 -4
  37. package/templates/tests/wecom-org-sync.spec.ts +7 -1
  38. package/vendor/host-versions.json +3 -3
package/devkit/README.md CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  面向用 `ojm` 开发前后端一体工程的开发者与 AI agent。本目录是**源文件**,
4
4
  发布时由 cli 的 `prepack`(`packages/cli/scripts/sync-devkit.mjs`)把其中
5
- **ojm 侧的 8 个文件**同步进 `packages/cli/devkit/`,随 `@oj-module/cli`
5
+ **ojm 侧的 9 个文件**同步进 `packages/cli/devkit/`,随 `@oj-module/cli`
6
6
  发布,再由 `ojm init` 分发到工程内。
7
7
 
8
8
  整棵树覆盖前后端两侧,但仍是**两套独立分发的 skill**(同名不同物,别混):
9
9
 
10
10
  | 侧 | 本仓位置 | 用户工程里的落点 | 分发链 |
11
11
  |---|---|---|---|
12
- | ojm 模块开发(前端模块、契约、前后端对接) | 本目录顶层 8 个文件 | `.claude/skills/ojm-module-dev/` | `@oj-module/cli` 包 → `ojm init` 第 4b 步 |
12
+ | ojm 模块开发(前端模块、契约、前后端对接) | 本目录顶层 9 个文件 | `.claude/skills/ojm-module-dev/` | `@oj-module/cli` 包 → `ojm init` 第 4b 步 |
13
13
  | oj API 开发(后端 handler、全局对象、配置、测试) | `oj-api-dev/` 子目录(快照) | `.claude/skills/oj-api-dev/` | **oj 二进制自带**(`bin/devkit/`)→ `ojm init` 第 4 步 |
14
14
 
15
15
  > **`oj-api-dev/` 子目录是快照,不是本仓维护的文档**:它与本机 oj 的
@@ -29,7 +29,8 @@
29
29
  | `module-dev-and-cdn-playground.md` | 教学文档:开发态 ≠ 分发态、「怎么安装自己的模块 / 会不会重复」、本地 CDN 市场链路五步跑通(setup → publish → build-index → verify → 安装) |
30
30
  | `sql-builder-missing-capabilities-pr.md` | 构造器能力缺口 PR 参考(**仅框架仓库**):7 项诉求的闭环记录 |
31
31
  | `menu-rules.md` | 方案菜单归组规则专题:单根模块约定、自定义根路径 `web.route_prefix`、`category` 三值语义、admin 重写与视觉归组(手册 §13 的展开版) |
32
- | `wecom-daikaifa.md` | 企业微信代开发集成:**凭据清单**(6 项 `vars` + 可信域名/可信 IP 备案)、首次开通顺序(含 `suite_ticket` 依赖)、**用户 / 开发 / 部署三角色手册**、错误文案速查 |
32
+ | `wecom-daikaifa.md` | 企业微信代开发集成:**凭据清单**(8 项 `vars`,按 Tier A/B/C 分层的来源与必填时序 + 可信域名/可信 IP 备案)、首次开通顺序(含 `suite_ticket` 依赖与引导期死锁的破法)、**用户 / 开发 / 部署三角色手册**、错误文案速查、与自建应用共存的语义变化 |
33
+ | `wecom-internal-app.md` | 企业微信**企业内部应用(自建应用)**集成:5 项 `wecom_corp_*` 凭据 + 2 项复用、回调只填「通讯录同步」一处、「验证并接入」四步自检、可见范围与可信 IP/域名、单企业私有化的边界(自建应用收不到成员变更事件)、共存配置、故障速查 |
33
34
 
34
35
  后端 handler 本身的写法(`json` 信封、`db` 查询、`manifest.yaml`、`oj test`)、
35
36
  站点挂载(`mounts`)、密钥信封(`secrets`)、vars、cors、schema 校验、broker/tasks/oidc/ws 等
@@ -71,7 +72,7 @@
71
72
  ```sh
72
73
  # 本 devkit(ojm 模块开发):拷入工程的 Claude Code skill 目录
73
74
  mkdir -p .claude/skills/ojm-module-dev
74
- cp SKILL.md manual.md README.md wecom-daikaifa.md .claude/skills/ojm-module-dev/
75
+ cp SKILL.md manual.md README.md wecom-daikaifa.md wecom-internal-app.md .claude/skills/ojm-module-dev/
75
76
 
76
77
  # 后端 API 开发另装 oj 自带的 devkit(独立 skill):
77
78
  # 权威源随 oj 二进制分发——本机安装在 ~/.local/share/oj/devkit/,
@@ -91,7 +92,8 @@ cp ~/.local/share/oj/devkit/SKILL.md ~/.local/share/oj/devkit/api-manual.md .cla
91
92
  `api-manual.md` + 有则拷的 `sql-builder-guide.md` / `oj-test-guide.md` +
92
93
  `global.d.ts` → `.claude/skills/oj-api-dev/`;
93
94
  - **第 4b 步**:从 cli 包内置 `devkit/`(prepack 由本目录顶层同步)拷
94
- `SKILL.md` / `manual.md` / `README.md` / `wecom-daikaifa.md` → `.claude/skills/ojm-module-dev/`。
95
+ `SKILL.md` / `manual.md` / `README.md` / `wecom-daikaifa.md` / `wecom-internal-app.md`
96
+ → `.claude/skills/ojm-module-dev/`。
95
97
 
96
98
  ```mermaid
97
99
  flowchart LR
@@ -110,6 +112,6 @@ flowchart LR
110
112
  框架贡献者要把本仓快照同步到新版:从 `~/.local/share/oj/devkit/` 整目录覆盖
111
113
  `docs/devkit/oj-api-dev/`(10 个文件,保持逐字节一致以便对账)。
112
114
 
113
- > 本目录顶层 8 个文件是**自包含**的:业务工程拿到即可读懂,不需要工程仓库里的
115
+ > 本目录顶层 9 个文件是**自包含**的:业务工程拿到即可读懂,不需要工程仓库里的
114
116
  > 其它文档。`oj-api-dev/` 子目录是 oj 侧快照,同样自包含(其自身 README 即索引),
115
117
  > 但不随 cli 包发货、仅本仓参考。维护者视角的内容来源整理在 `framework-dev.md`(仅本仓)。
package/devkit/SKILL.md CHANGED
@@ -18,7 +18,7 @@ description: 用 ojm 开发前后端一体工程时使用——新增/修改前
18
18
  1. **读章**:建模块 → 手册 §2;加接口(handler .schema → oj openapi → 生成 → 对接)→ §3;
19
19
  接管通知/上传/菜单 → §4;换布局或加插槽 → §5;排查 → §8;
20
20
  **组织架构 / 通讯录(选人、按部门算收件人、接 LDAP 等新来源)→ §14**;
21
- **接通企业微信代开发(企微登录 / 多企业租户 / 代开发后台 / 通讯录同步)→ `wecom-daikaifa.md`**。
21
+ **接通企业微信(代开发 / 企业内部应用 / 两套共存)→ `wecom-daikaifa.md` + `wecom-internal-app.md`**。
22
22
  2. **建目录**:一个目录 = 一个模块——前端一侧 `src/<模块名>/web/`(`entry.ts` 必备,
23
23
  `pages/`、`locales/` 按需),后端一侧 `src/<模块名>/api/`(目录镜像路由的 `api.ts`,
24
24
  动词挂 `.schema`)。两侧可只存在一侧。
@@ -101,19 +101,34 @@ description: 用 ojm 开发前后端一体工程时使用——新增/修改前
101
101
  | 改 `schema.yaml` / `migrations/` / `config.yaml` | **必须重启** |
102
102
  | 前端 `src/<模块>/web/**` | 模块重建 + 浏览器自动刷新 |
103
103
 
104
- ## 企业微信代开发(接通真实套件)
104
+ ## 企业微信(接通真实套件 / 自建应用)
105
105
 
106
- 模板已内置企微登录(`src/auth/api/wecom/` + 登录页「企业微信」页签 + `/system/wecom` 代开发后台)。
107
- 接真实套件的**凭据清单、开通顺序、用户/开发/部署三视角、错误文案速查**全在
108
- `wecom-daikaifa.md`(同目录),这里只列最容易踩的四条:
106
+ 模板已内置企微登录(`src/auth/api/wecom/` + 登录页「企业微信」页签 + `/system/wecom` 后台)。
107
+ 两种来源、两本手册,**按客户形态选**:
109
108
 
110
- - **六项 `vars` 任一为空 → 企微登录整体 400**(`wecom_suite_id` / `wecom_suite_secret` /
111
- `wecom_callback_token` / `wecom_encoding_aes_key` / `wecom_enc_key` / `wecom_base_url`)。
112
- `wecom_base_url` 必须以 `/api/auth/wecom` 结尾,它派生 `<base>/event`(指令回调 URL)与 `<base>/callback`。
109
+ | 客户形态 | 读 | 凭据 |
110
+ |---|---|---|
111
+ | 多家企业(SaaS,扫码即装) | `wecom-daikaifa.md`(同目录) | 代开发套件 8 项 `vars`(Tier A/B/C) |
112
+ | 单企业私有化 | `wecom-internal-app.md`(同目录) | 自建应用 5 项 `wecom_corp_*` + 2 项复用 |
113
+ | 两者同时 | 两本都读,共存看 `wecom-daikaifa.md` §10 | 两套都填 + `wecom_allow_coexist: 'true'` |
114
+
115
+ 两本手册都有**凭据清单、开通顺序、用户/开发/部署三视角、错误文案速查**,接企微时先读它们。
116
+ 这里只列最容易踩的五条:
117
+
118
+ - **凭据填齐 = 启用,没有 `wecom_mode`**。只看「哪套变量填了」:`wecom_suite_id` + `wecom_suite_secret`
119
+ = 代开发;`wecom_corp_id` + `wecom_corp_secret` + `wecom_corp_agent_id` = 自建。两套都齐必须显式
120
+ `wecom_allow_coexist: 'true'`,否则首个请求 400(配置事故要看得见,不允许静默择一)。
121
+ `wecom_enc_key` / `wecom_base_url` 两种来源都要;`wecom_base_url` 必须以 `/api/auth/wecom` 结尾,
122
+ 它派生 `<base>/event`(回调 URL)与 `<base>/callback`(网页授权回跳)。
113
123
  - **`suite_ticket` 是被企微推来的**(每 10 分钟一次、30 分钟过期),所以「回调 URL 打通」必须
114
- 早于「生成开通链接」,否则 503 且看起来像代码坏了。
115
- - **新增企业不改配置、不重启**:企业管理员确认授权 → `create_auth` → `wecom_app_profile`。
124
+ 早于「生成开通链接」,否则 503 且看起来像代码坏了。自建模式没有 `suite_ticket`。
125
+ - **新增企业不改配置、不重启**(代开发):企业管理员确认授权 → `create_auth` → `wecom_app_profile`。
116
126
  同企业测试+正式两个应用是两行(主键含 `agent_id`),此时登录链接必须带 `&agent=`。
127
+ **自建模式反过来**:一套部署一家企业,档案行由 `/system/wecom` 点「验证并接入」写入;
128
+ 换过 Secret 必须重新点一次。
129
+ - **自建应用收不到成员变更事件**(官方 90970):要事件增量,只能让企业管理员另配
130
+ 「通讯录同步 → 接收事件服务器」,且该 URL 必须在企业主体域名下——这就是「SaaS 多企业只能走代开发」的由来。
131
+ 一个 `/event` 端点吃两套回调,靠逐个 Token 试签分辨,两组凭据不能串用。
117
132
  - **整页跳转拼 API base**:`window.location.assign(resolveApiBase() + wecomAuthorizePath(corp))`;
118
133
  拼站点 base 会被 SPA 兜底页吞掉(页面原地重载、一个请求都不发,控制台无报错)。
119
134
  - **通讯录同步是「组织架构底座 + 企微适配器」,不是企微专属功能**:模板的
@@ -200,5 +215,10 @@ description: 用 ojm 开发前后端一体工程时使用——新增/修改前
200
215
  14 组织架构与通讯录(共享读面 7 端点、`scope` 两级门禁、归属权与「转为本地维护」、
201
216
  源适配器接点与三条禁令——选人 / 按部门算人 / 接 LDAP 都读它)。
202
217
 
203
- 另有 `wecom-daikaifa.md`(同目录):企业微信代开发集成的凭据清单与三角色手册——凭据从哪取、
204
- 怎么填、按什么顺序开通、用户/开发/部署各自怎么做、错误文案怎么查。**接企微时先读它**。
218
+ 另有 `wecom-daikaifa.md`(同目录):企业微信**代开发**集成的凭据清单与三角色手册——凭据从哪取、
219
+ 怎么填、按什么顺序开通、用户/开发/部署各自怎么做、错误文案怎么查。**接代开发时先读它**。
220
+
221
+ 另有 `wecom-internal-app.md`(同目录):企业微信**企业内部应用(自建应用)**的配置与使用手册——
222
+ 五项 `wecom_corp_*` 从哪取、回调为什么只填「通讯录同步」一处、「验证并接入」的四步自检、
223
+ 可见范围 / 可信 IP / 可信域名的处置、单企业私有化的边界(自建应用收不到成员变更事件)、
224
+ 以及和代开发共存时的配置与页面差异。**接自建应用时先读它**。
package/devkit/manual.md CHANGED
@@ -1569,7 +1569,7 @@ export default async (path: string, html: string) => {
1569
1569
  | 限速/节流/去重窗口在真实请求下永不累积(测试却全绿) | 模块级 `Map`/`let` 不跨请求存活——oj 每请求重建模块(§1.7-3) | 状态落库(模板 0009 先例)或走 oj 持久面 |
1570
1570
  | `ojm build` 报 `introspect api.ts: Error: jwt not configured`(dev 模式正常) | 模块级语句触碰运行时全局,introspect 空沙箱里炸(§1.7-4) | 移进 handler 函数体内按需执行 |
1571
1571
  | 点登录页「企业微信」后页面只闪一下、Network 里一个 auth 请求都没有 | 整页跳转拼成了**站点 base**(根部署下得到 `/auth/wecom/login`,被 SPA 兜底页吞掉) | 改用 `resolveApiBase()` 拼后端端点:`window.location.assign(resolveApiBase() + wecomAuthorizePath(corp))` |
1572
- | 企微登录恒 400 `企业微信未配置:config vars 缺少 …` | 部署常量没填全(六项任一为空即整体拒服务) | 见 `wecom-daikaifa.md` §2.1 的凭据表与缺项后果 |
1572
+ | 企微登录恒 400 `企业微信未配置:config vars 缺少 …` | 部署常量没填全(该来源的凭据任一为空即整体拒服务) | 代开发见 `wecom-daikaifa.md` §2.1,企业内部应用见 `wecom-internal-app.md` §2.1 的凭据表与缺项后果 |
1573
1573
 
1574
1574
  ---
1575
1575
 
@@ -1597,9 +1597,12 @@ export default async (path: string, html: string) => {
1597
1597
  写 handler 遇到 `json` / `db` / `kv` / `jwt` / 租户 / 测试的问题查它;
1598
1598
  站点挂载(`mounts`)、密钥信封(`secrets`)、配置变量(`vars`)、跨域(`cors`)、
1599
1599
  schema 校验(`schema_validation`)、`broker` / `tasks` / `oidc` / `ws` 等 oj 侧能力也都在这套手册里。
1600
- - **企业微信代开发对接**:本目录 `wecom-daikaifa.md`——六项部署常量从哪取/怎么填、
1600
+ - **企业微信代开发对接**:本目录 `wecom-daikaifa.md`——八项部署常量(Tier A/B/C)从哪取/怎么填、
1601
1601
  可信域名与可信 IP 备案、首次开通顺序(`suite_ticket` 依赖回调先通)、
1602
- 用户 / 开发 / 部署三种角色各自的操作、按错误文案索引的故障速查。接企微时先读它。
1602
+ 用户 / 开发 / 部署三种角色各自的操作、按错误文案索引的故障速查、与自建应用共存的语义变化。
1603
+ - **企业微信企业内部应用(自建)对接**:本目录 `wecom-internal-app.md`——五项 `wecom_corp_*` 从哪取、
1604
+ 回调只填「通讯录同步 → 接收事件服务器」一处、「验证并接入」的四步自检、可见范围决定同步范围、
1605
+ 单企业私有化的边界(自建应用收不到成员变更事件)、与代开发共存的配置。接企微时按客户形态选一本。
1603
1606
  - **框架团队内部**:本目录 `framework-dev.md`(改 `packages/runtime`、`packages/cli` 用,
1604
1607
  业务工程不适用)。
1605
1608
 
@@ -17,10 +17,17 @@
17
17
  | 企业管理员(客户的 IT) | §2.5 §2.6 §6.8 §7.6-B §8 | 开四项通讯录权限、选对授权模式、读懂同步页面上的卡片 |
18
18
  | 企业员工(用户) | §5 | 怎么进、要不要注册、进不去怎么办 |
19
19
 
20
+ > 本文件只讲**代开发**。要把同一套部署接成一家企业的**企业内部应用(自建应用)**,
21
+ > 或让两种来源同时服务,读同目录 `wecom-internal-app.md`;两者共存的配置与页面差异见本文 **§10**。
22
+
20
23
  ---
21
24
 
22
25
  ## 1. 这套东西在做什么
23
26
 
27
+ > **术语先分清**:代开发应用在客户企微后台里**也显示为一个「自建应用」**,区别在于它的权限与回调
28
+ > 都由服务商的模板决定、由企业管理员扫码授权装配,而不是客户自己建。客户口中的「自建应用」到底指
29
+ > 哪一种,第一次对接时必须问清——两者本文只讲前者,后者见 `wecom-internal-app.md`。
30
+
24
31
  一句话:**企业管理员在自己企业里「授权安装」一次,之后该企业员工就能用企微身份直接登录
25
32
  本站,不需要注册**。本站同时服务多个企业,靠租户列隔离数据。
26
33
 
@@ -86,7 +93,8 @@
86
93
  `get_customized_auth_url`,该接口只认 `provider_access_token`,而它必须由服务商 corpid + provider_secret 换得。
87
94
  缺它**不影响登录**(login 走 suite 流程),但 `/system/wecom` 点「生成开通链接」会 400 点名缺这两项。
88
95
 
89
- Tier A 任一为空 → 服务启动即 400 拒绝(宁可不登录,也不明文存 `permanent_code`)。
96
+ Tier A 任一为空 → 首个企微相关请求即 400 拒绝(配置按请求读取,不是启动期校验;宁可不登录,
97
+ 也不明文存 `permanent_code`)。
90
98
  Tier B 为空时服务仍可启动并响应 echostr,但 suite 流程会 400——先配 Tier A 把回调验证跑通、
91
99
  创建模版、拿到 suite 凭据,再补全 Tier B 即可,无需鸡生蛋。Tier C 可与 Tier B 同一阶段补全。
92
100
 
@@ -679,3 +687,89 @@ grep -h "wecom change_contact\|wecom event ignored\|callback decrypt failed" log
679
687
  [ ] /system/departments 顶部状态条有「更新于 X」;树有名字、人挂在正确部门
680
688
  [ ] db.sqlite 与 wecom_enc_key 各自有备份
681
689
  ```
690
+
691
+ ---
692
+
693
+ ## 10. 与「企业内部应用(自建)」共存
694
+
695
+ 本部署可以同时接两种来源:**代开发**(服务商模板,扫码即装、可服务多家企业)与
696
+ **企业内部应用 / 自建应用**(企业自建,一套部署一家企业)。自建侧的五项凭据、接入流程、
697
+ 硬约束在同目录 `wecom-internal-app.md`;本节只讲「两者同时存在时」本文件哪些行为会变。
698
+
699
+ ### 10.1 什么时候需要共存
700
+
701
+ | 场景 | 说明 |
702
+ |---|---|
703
+ | 本站自己用自建应用 + 子公司 / 客户走代开发 | 最常见。两套档案的租户键都是 `corp_id`,天然隔离 |
704
+ | 迁移期:同一家既有代开发应用、又刚建了自建应用 | 需要清单里能分辨、事件能选对密钥、同步能带 agent |
705
+ | 只是从代开发迁到自建 | 不需要长期共存,按 §10.6 迁完清变量即可 |
706
+
707
+ ### 10.2 三行配置
708
+
709
+ ```yaml
710
+ # 代开发那套(不变,见 §2.1)
711
+ wecom_suite_id: 'ww…'
712
+ wecom_suite_secret: '…'
713
+ wecom_callback_token: '…'
714
+ wecom_encoding_aes_key: '…'
715
+
716
+ # 自建那套(见 wecom-internal-app.md §2.1)
717
+ wecom_corp_id: 'ww…'
718
+ wecom_corp_secret: '…'
719
+ wecom_corp_agent_id: '1000002'
720
+
721
+ # 两套凭据都填齐时必须显式开,否则首个请求 400
722
+ wecom_allow_coexist: 'true' # 取值 1 / true / yes
723
+ ```
724
+
725
+ 不开就报:`企业微信未配置:config vars 缺少 wecom_allow_coexist: 'true'(检测到代开发与自建应用两套凭据同时配置;确认要共存请显式开启)`。
726
+ 这是故意的:从代开发迁到自建却忘了清旧变量,属于配置事故,应当被看见而不是被静默择一。
727
+
728
+ 「启用哪几种来源」的判定**没有** `wecom_mode` 这类变量,只看「哪套凭据填齐了」——
729
+ 变量填了没有是客观事实,不会与运行期能力漂移。
730
+
731
+ ### 10.3 页面与清单的变化
732
+
733
+ - `/system/wecom` 两张卡片按需显示:「开通企业」(代开发,生成开通链接)与
734
+ 「企业内部应用(自建)」(验证并接入)。两者都在时顶部多一条「本部署同时启用」提示。
735
+ - 已开通企业清单新增**来源**列(代开发 / 企业内部应用);「授权模式」列对自建行显示 `—`
736
+ (自建没有「管理员授权 / 成员授权」之分,同步闸门恒通过)。
737
+ - 部署配置自检未通过时,页面顶部红条直接说出缺什么,而不是整页失败。
738
+
739
+ ### 10.4 两组回调凭据:同一个 URL,不同源,不能互相顶替
740
+
741
+ | 位置 | 用哪组凭据 | 谁填 |
742
+ |---|---|---|
743
+ | 代开发「指令回调 URL」(本文 §3) | `wecom_callback_token` / `wecom_encoding_aes_key` | 服务商在模板详情页填 |
744
+ | 自建「通讯录同步 → 接收事件服务器」 | `wecom_corp_callback_token` / `wecom_corp_encoding_aes_key` | 企业管理员在自己后台填 |
745
+
746
+ 两个地址**都是** `<wecom_base_url>/event`,服务端逐个 Token 试签分辨(SHA1 一次,成本可忽略),
747
+ 命中哪套就用哪套钥匙解密。所以:
748
+
749
+ - 不需要为自建应用再开一条路由,匿名路径白名单也不用加;
750
+ - 但两组凭据**不能串用**:把代开发那组填进自建的位置,只会在保存 URL 时验签失败;
751
+ - 排障时日志会带 `kind`:`kind=suite` / `kind=corp` 告诉你这条报文是哪一侧来的,别再靠猜。
752
+
753
+ ### 10.5 共存下代开发的语义变化(三处,都是「别误伤邻居」)
754
+
755
+ | 动作 | 现在只影响 | 原因 |
756
+ |---|---|---|
757
+ | 企业取消授权(`cancel_auth`) | 只停 `credential_kind='suite'` 的行;该企业仍有其它活跃应用时**不吊销会话** | 代开发取消授权不该把同一家企业的自建应用一起停掉 |
758
+ | 授权模式复核(`change_auth`) | 只遍历 suite 行 | 自建行没有 `auth_mode` 概念 |
759
+ | 组织架构同步 | 状态视图会回传 `agentId`,前端 start / cancel / status 原样带上 | 同一 `corp_id` 有两行时,不指定 agent 后端只能 400 |
760
+
761
+ 同一企业双应用的登录链接**要带 agent**(`/auth/wecom/login?corp=ww…&agent=…`);只带 `corp`
762
+ 会 400 `该企业开通了多个应用,登录链接必须带 agent 参数`。不带 `corp` 时按「本站自己那家企业」
763
+ 解析:**配了 `wecom_corp_*` 就把候选范围限定在那家企业**(agent 取配置里的),没配才从已装档案里解析。
764
+ 所以共存部署里「配了 corp 但还没点验证并接入」时,本站 `/login` 会 400
765
+ `本次部署配置的企业内部应用尚未接入:请在后台点「验证并接入」`——这是提示不是缺陷。
766
+
767
+ ### 10.6 迁移:代开发 → 自建(或反过来)
768
+
769
+ 1. 备好自建应用三项(+ 可选通讯录回调两项),填进 `vars`,重启;
770
+ 2. `/system/wecom` 点「验证并接入」→ 清单多一行 `credential_kind=corp`;
771
+ 3. 用一个普通员工验证登录(账号仍是 `wecom:<userid>`,数据不丢);
772
+ 4. 确认无误后清空 `wecom_suite_*` / `wecom_provider_*` / `wecom_callback_token` /
773
+ `wecom_encoding_aes_key`,去掉 `wecom_allow_coexist`,重启;
774
+ 5. 清单里代开发那几行**停用而不删**,留作回滚。
775
+