liu
2026-09-30 6774e2d2a4442cc8485d60ff34f76fcf1496f5c7
docs(wxcp): 补 wxcp_user 默认角色的建角色/授权 SQL 与部署说明

企业微信扫码自动注册的账号默认绑定角色编码 wxcp_user
(见 AdminAuthServiceImpl.WXCP_DEFAULT_ROLE_CODE),该角色必须先在库里存在,
否则账号能登录但 get-permission-info 返回空菜单,进去一片空白。

- wxcp_menu.sql / config_export_all_20260929.sql:建角色 + 授「会员管理」
整棵子树(根菜单 1075490,共 15 条)。刻意放在阶段三末尾 —— 阶段二会
DELETE FROM system_role 从源库重拷角色,放前面会被清掉。
- wxcp_login_integration.md:补默认权限规则、以及「已建过的老库要补执行
这一段」的提示。

Co-Authored-By: Claude <noreply@anthropic.com>
已修改3个文件
131 ■■■■■ 文件已修改
docs/sql/config_export_all_20260929.sql 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/sql/wxcp_menu.sql 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/wxcp_login_integration.md 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/sql/config_export_all_20260929.sql
@@ -11634,4 +11634,69 @@
--   WHERE `r`.`id` IN (1, 160, 2)   -- 追加给「普通角色」
-- ============================================================
-- ============================================================
-- 企业微信扫码自动注册账号的默认角色:wxcp_user
-- (与 docs/sql/wxcp_menu.sql 末尾同一段,保持同步)
--
-- 必须放在本文件阶段二的 `DELETE FROM system_role` 之后执行,
-- 否则角色会被阶段二从源库重拷时清掉 —— 这也是它落在阶段三末尾的原因。
--
-- 背景:企业微信首次扫码走「自动注册」建号,建完按「角色编码」查本段建出来的
--       这个角色并绑定。代码常量见 AdminAuthServiceImpl.WXCP_DEFAULT_ROLE_CODE。
--       角色不存在时代码只打 warn 日志、不阻断登录 —— 账号能进但一个菜单都没有,
--       属于「能登录却什么都看不到」的静默故障。
--
-- 授予范围:仅「会员管理」整棵子树(根菜单编号 1075490,定义见 ums_module.sql)。
--           首页是前端静态路由、不依赖菜单表,任何账号都看得到,无需授权。
--
-- 幂等:角色先查后插;角色菜单关联先删后插,可重复执行。
-- ============================================================
-- 1) 建角色
INSERT INTO `system_role`
  (`name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `updater`)
SELECT '企业微信扫码用户', 'wxcp_user', 100, 1, '', 0, 2,
       '企业微信首次扫码自动注册的账号默认角色,仅授予「会员管理」模块',
       'admin', 'admin'
FROM DUAL
WHERE NOT EXISTS (
        SELECT 1 FROM (SELECT `id` FROM `system_role`
                       WHERE `code` = 'wxcp_user' AND `deleted` = b'0') `t`);
-- 2) 取角色编号(新建的与已存在的都取得到;取不到时为 NULL,下面两句自然不生效)
SET @wxcp_role_id = (SELECT `id` FROM `system_role`
                     WHERE `code` = 'wxcp_user' AND `deleted` = b'0' LIMIT 1);
-- 3) 授权:会员管理根菜单(1075490) + 子菜单 + 孙菜单,三级覆盖整棵子树
DELETE FROM `system_role_menu` WHERE `role_id` = @wxcp_role_id;
INSERT INTO `system_role_menu` (`role_id`, `menu_id`, `creator`, `updater`)
SELECT @wxcp_role_id, `m`.`id`, 'admin', 'admin'
FROM `system_menu` `m`
LEFT JOIN `system_menu` `p1` ON `p1`.`id` = `m`.`parent_id`
LEFT JOIN `system_menu` `p2` ON `p2`.`id` = `p1`.`parent_id`
WHERE `m`.`deleted` = b'0'
  AND (`m`.`id` = 1075490 OR `p1`.`id` = 1075490 OR `p2`.`id` = 1075490)
  AND @wxcp_role_id IS NOT NULL;
-- 4) 回填:给本段执行前就已存在的自动注册账号补上默认角色
--
-- 为什么必须有这一步:账号一旦写进 system_social_user_bind,再次扫码走的是
-- 「已绑定」分支,不会再进自动注册逻辑 —— 不补的话这批老账号永远没有菜单。
--
-- 只认自动注册的用户名前缀(wx_ + 8 位十六进制,与 AdminAuthServiceImpl 生成规则一致);
-- 用 ESCAPE 显式声明转义符,避免 `_` 被 LIKE 当成单字符通配。
-- system_user_role 没有 (user_id, role_id) 唯一约束,所以用 NOT EXISTS 兜幂等。
INSERT INTO `system_user_role` (`user_id`, `role_id`, `creator`, `updater`)
SELECT `u`.`id`, @wxcp_role_id, 'admin', 'admin'
FROM `system_users` `u`
WHERE `u`.`deleted` = b'0'
  AND `u`.`username` LIKE 'wx!_%' ESCAPE '!'
  AND @wxcp_role_id IS NOT NULL
  AND NOT EXISTS (
        SELECT 1 FROM (SELECT `user_id` FROM `system_user_role`
                       WHERE `role_id` = @wxcp_role_id
                         AND `deleted` = b'0') `t`
        WHERE `t`.`user_id` = `u`.`id`);
SET FOREIGN_KEY_CHECKS = 1;
docs/sql/wxcp_menu.sql
@@ -90,3 +90,67 @@
-- 换成目标角色编号即可,例如:
--   WHERE `r`.`id` IN (1, 160, 2)   -- 追加给「普通角色」
-- ============================================================
-- ============================================================
-- 企业微信扫码自动注册账号的默认角色:wxcp_user
--
-- 背景:企业微信首次扫码走「自动注册」建号,建完按「角色编码」查本段建出来的
--       这个角色并绑定。代码常量见 AdminAuthServiceImpl.WXCP_DEFAULT_ROLE_CODE,
--       改编码要两边一起改。
--       角色不存在时代码只打 warn 日志、不阻断登录 —— 账号能进,但一个菜单都没有,
--       是「能登录却什么都看不到」的静默故障,所以新库初始化必须带上这一段。
--
-- 为什么用编码不用角色编号:角色编号在本地/测试/生产各库不一致,写死换库就失效。
--
-- 授予范围:仅「会员管理」整棵子树(根菜单编号 1075490,定义见 ums_module.sql)。
--           首页是前端静态路由、不依赖菜单表,任何账号都看得到,无需授权。
--
-- 幂等:角色先查后插;角色菜单关联先删后插,可重复执行。
-- ============================================================
-- 1) 建角色
INSERT INTO `system_role`
  (`name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `updater`)
SELECT '企业微信扫码用户', 'wxcp_user', 100, 1, '', 0, 2,
       '企业微信首次扫码自动注册的账号默认角色,仅授予「会员管理」模块',
       'admin', 'admin'
FROM DUAL
WHERE NOT EXISTS (
        SELECT 1 FROM (SELECT `id` FROM `system_role`
                       WHERE `code` = 'wxcp_user' AND `deleted` = b'0') `t`);
-- 2) 取角色编号(新建的与已存在的都取得到;取不到时为 NULL,下面两句自然不生效)
SET @wxcp_role_id = (SELECT `id` FROM `system_role`
                     WHERE `code` = 'wxcp_user' AND `deleted` = b'0' LIMIT 1);
-- 3) 授权:会员管理根菜单(1075490) + 子菜单 + 孙菜单,三级覆盖整棵子树
DELETE FROM `system_role_menu` WHERE `role_id` = @wxcp_role_id;
INSERT INTO `system_role_menu` (`role_id`, `menu_id`, `creator`, `updater`)
SELECT @wxcp_role_id, `m`.`id`, 'admin', 'admin'
FROM `system_menu` `m`
LEFT JOIN `system_menu` `p1` ON `p1`.`id` = `m`.`parent_id`
LEFT JOIN `system_menu` `p2` ON `p2`.`id` = `p1`.`parent_id`
WHERE `m`.`deleted` = b'0'
  AND (`m`.`id` = 1075490 OR `p1`.`id` = 1075490 OR `p2`.`id` = 1075490)
  AND @wxcp_role_id IS NOT NULL;
-- 4) 回填:给本段执行前就已存在的自动注册账号补上默认角色
--
-- 为什么必须有这一步:账号一旦写进 system_social_user_bind,再次扫码走的是
-- 「已绑定」分支,不会再进自动注册逻辑 —— 不补的话这批老账号永远没有菜单。
--
-- 只认自动注册的用户名前缀(wx_ + 8 位十六进制,与 AdminAuthServiceImpl 生成规则一致);
-- 用 ESCAPE 显式声明转义符,避免 `_` 被 LIKE 当成单字符通配。
-- system_user_role 没有 (user_id, role_id) 唯一约束,所以用 NOT EXISTS 兜幂等。
INSERT INTO `system_user_role` (`user_id`, `role_id`, `creator`, `updater`)
SELECT `u`.`id`, @wxcp_role_id, 'admin', 'admin'
FROM `system_users` `u`
WHERE `u`.`deleted` = b'0'
  AND `u`.`username` LIKE 'wx!_%' ESCAPE '!'
  AND @wxcp_role_id IS NOT NULL
  AND NOT EXISTS (
        SELECT 1 FROM (SELECT `user_id` FROM `system_user_role`
                       WHERE `role_id` = @wxcp_role_id
                         AND `deleted` = b'0') `t`
        WHERE `t`.`user_id` = `u`.`id`);
docs/wxcp_login_integration.md
@@ -93,6 +93,7 @@
| 场景 | 规则 |
|------|------|
| 企业微信首次扫码(未绑定) | 自动创建系统用户,用户名为 `wx_` + 8 位随机十六进制,昵称取企业微信昵称,账号状态为启用,随机生成密码(用户无需知晓,仅用于占位) |
| 首次注册后的默认权限 | 绑定角色 `wxcp_user`,**仅授予「会员管理」整棵子树**(根菜单 1075490 + 子菜单 + 孙菜单,共 15 条)。首页是前端静态路由、不依赖菜单表,任何账号都可见,无需授权。角色缺失时代码只记 warn 日志、不阻断登录,表现为「能进系统但菜单全空」 |
| 企业微信再次扫码(已绑定) | 直接登录,不创建新用户 |
| 非企业微信的社交平台首次登录 | 仍返回 `1_002_000_005`「未绑定账号,需要进行绑定」,需引导用户走账号密码登录并绑定 |
| 账号密码登录时携带社交参数 | 走原有绑定逻辑,登录成功后该社交账号与账号绑定 |
@@ -104,6 +105,7 @@
- 企业微信授权登录要求**已备案域名**且备案主体与企业微信认证主体一致,本地 `localhost` 调试需用内网穿透并保证域名备案。
- 首次自动注册会跳过"注册功能已关闭"配置项(`system.user.register-enabled`),但**仍受租户账号配额限制**,配额用尽时返回 `USER_COUNT_MAX`。
- 自动注册生成的用户名以 `wx_` 开头,便于与人工创建的账号区分;如需限制企业微信登录的用户范围,需在业务侧按用户名前缀或角色做二次管控。
- **默认角色 `wxcp_user` 必须先在库里存在**。该角色及其「会员管理」授权已写进 `docs/sql/wxcp_menu.sql` 末尾(合并脚本 `config_export_all_<日期>.sql` 阶段三末尾同步了一份),新库初始化会自动建出来;**已建过的老库要补执行这一段**,否则扫码新账号能登录但菜单空白。代码按**角色编码**查找而非角色编号,换库不会失效。
- 回调接口 `/admin-api/wxcp/callback` 已加入免登录白名单,**不需要也不应该带前端令牌**。
- 前端原有的「未绑定 → 弹出绑定账号密码弹窗」逻辑,对企业微信场景不再触发;若产品上仍希望首次登录先绑定既有账号,需要另行明确,当前实现为直接自动注册。