.claude/rules/sql-config-export-init.md
@@ -24,7 +24,7 @@ | 阶段 | 内容 | 数据来源 | |------|------|----------| | 阶段一(原 config_export) | 全库 413 张表 `CREATE TABLE` + 6 类配置数据:①协同审批流程(act_*/bpm 激活流程元数据+定义)②协同业务配置(bpm 附件/分类/表单/用户组/监听/表达式)③BI 大屏/go-view ④MES 编码规则 ⑤系统配置主数据(system_dict_type/data、tenant、company_config)⑥AI/基础设施配置。**刻意排除身份(users)/菜单/日志/流水**。 | ruoyi-vue-pro 字面量导出(自包含) | | 阶段二(原 test_identity_seed) | 补身份 + 菜单最小可登录数据:①`system_menu` 全部有效菜单 ②`system_dept`/`post`(仅 deleted=0)③`system_role` 超管 super_admin ④`system_users` admin ⑤`system_user_role` ⑥`system_oauth2_client`(client_id='default')。幂等:先 `DELETE` 再 `INSERT`。 | ruoyi-vue-pro 跨库 `INSERT..SELECT` | | 阶段二(原 test_identity_seed,已扩展为完整身份/权限) | 补导全部有效身份 + 菜单 + 权限:①`system_menu` 全部有效菜单 ②`system_dept`/`post`(仅 deleted=0)③`system_role` 全部有效角色(含 super_admin)④`system_users` 全部未删除账号(含 admin)⑤`system_user_role`(按已拷贝用户交集)⑥`system_role_menu`(按已拷贝用户所属角色交集,普通角色登录依赖)⑦`system_oauth2_client`。幂等:先 `DELETE` 再 `INSERT`。 | ruoyi-vue-pro 跨库 `INSERT..SELECT` | 日常初始化:只需在目标空库执行 `config_export_all_20260904.sql` 这一份。 @@ -49,7 +49,7 @@ ## 关键约束与坑(避免踩) - ⚠ 阶段一含**全库 `DROP TABLE IF EXISTS`**,会清空非其建表的所有表数据,**只能在全新/可覆盖空库执行**,禁止在运行库直接跑。 - ⚠ 超管 `super_admin` 登录后由代码**直接返回全部菜单**,故**不需要 `system_role_menu` 行**;但 `system_menu` 表必须有数据。若未来要支持普通角色登录,则需补该角色 → `system_role_menu` → `system_user_role` 全链路。 - ⚠ 超管 `super_admin` 登录后由代码**直接返回全部菜单**;普通角色登录则依赖 `system_role_menu`。阶段二现按已拷贝用户所属角色补导 `system_role_menu`,普通角色亦可正常登录。 - ⚠ 登录/刷新令牌**必须**有 `system_oauth2_client` 中 `client_id='default'` 的记录,否则 `validOAuthClientFromCache` 报错登不上。 - 字典 `system_dict_type`/`system_dict_data` 由阶段一导入,阶段二不重复处理。 - 源库 `system_dept`/`system_post` 含较多软删除行,阶段二仅拷贝 `deleted=0` 的有效行。 docs/sql/config_export_all_20260904.sql
@@ -10,6 +10,7 @@ -- ③ BI 决策大屏 / go-view:bi_dashboard、bi_chart_config、bi_data_source_config、bi_warehouse_coordinate、report_go_view_project -- ④ MES 编码规则(剔流水记录):mes_md_auto_code_rule、mes_md_auto_code_part -- ⑤ 系统配置主数据(字典/租户/公司配置;剔身份/菜单/日志):system_dict_type、system_dict_data、system_tenant、system_tenant_package、system_company_config -- ⚠ 身份/权限类表(system_users/role/menu/role_menu/user_role/dept/post/oauth2_client)此处仅建空表结构,数据由文件末尾"阶段二"跨库补导。 -- ⑥ AI 配置(仅模型+key)+ 基础设施配置管理:ai_model、ai_api_key、infra_config、infra_job、infra_file_config、infra_data_source_config、infra_codegen_table、infra_codegen_column -- 过滤:协同流程仅保留激活(SUSPENSION_STATE_=1)定义及其部署/字节/模型/扩展信息; -- 编码规则剔除流水记录 mes_md_auto_code_record;软删除表仅导 deleted=0。 @@ -11184,21 +11185,22 @@ -- ======================================================================= -- ======================================================================= -- 文件:test_identity_seed_20260904.sql -- 用途:给 test 库补齐"身份 + 菜单"最小可登录数据(超管),数据源:ruoyi-vue-pro -- 文件:test_identity_seed_20260904.sql(admin 单账号身份/权限补导) -- 用途:给 test 库补齐"admin 账号 + 菜单 + 权限"数据,数据源:ruoyi-vue-pro -- 前提:test 库已执行过 config_export_20260904.sql(全表结构已存在,字典已导); -- 本脚本须在“同时存在 ruoyi-vue-pro 与 test”的同一 MySQL 实例上执行。 -- 覆盖: -- ① system_menu 全部有效菜单(超管登录后由代码直接返回全部菜单,故不需要 role_menu 行) -- ② system_dept / post 源库未删除的有效部门/岗位行 -- ③ system_role 超管角色 super_admin -- ④ system_users admin 超管账号(密码哈希一并拷贝,登录密码同源库) -- ⑤ system_user_role admin → super_admin 关联 -- ⑥ system_oauth2_client 默认 OAuth2 客户端(登录/刷新令牌必需,client_id='default') -- 覆盖(仅 admin 一个账号,及其拥有的角色与权限): -- ① system_menu 全部有效菜单(超管由代码返回全部菜单,普通角色依赖 role_menu) -- ② system_dept / post 源库未删除的有效部门/岗位行(admin 的 dept_id 落在其中) -- ③ system_role 仅 admin 拥有的角色(super_admin、common、001、crm_admin 等) -- ④ system_users 仅 admin 账号(密码哈希一并拷贝,登录密码同源库) -- ⑤ system_user_role admin 与其角色的关联 -- ⑥ system_role_menu admin 各角色的菜单关联(保证普通角色权限正常) -- ⑦ system_oauth2_client 默认 OAuth2 客户端(登录/刷新令牌必需,client_id='default') -- 字典 system_dict_type / system_dict_data:config_export 已导入(test 现为 241/1157),此处不重复处理。 -- 幂等:每张表先 DELETE 再 INSERT,可重复执行;不影响其它表。 -- ⚠ 仅作用于上述列出的表,不会清空其它表数据。 -- ⚠ 源库 ruoyi-vue-pro 的 system_dept / system_post 含较多软删除行,此处仅拷贝未删除(deleted=0)的。 -- ⚠ 源库 system_dept / system_post / system_users / system_role 含软删除行,仅拷贝未删除(deleted=0)的。 -- ======================================================================= SET NAMES utf8mb4; @@ -11228,22 +11230,30 @@ SELECT * FROM `ruoyi-vue-pro`.`system_post` WHERE `deleted` = b'0'; -- ③ 超管角色(super_admin) -- ③ admin 拥有的角色 DELETE FROM `system_role`; INSERT INTO `system_role` SELECT * FROM `ruoyi-vue-pro`.`system_role` WHERE `code` = 'super_admin' AND `deleted` = b'0'; WHERE `deleted` = b'0' AND `id` IN (SELECT `role_id` FROM `ruoyi-vue-pro`.`system_user_role` WHERE `user_id` = (SELECT `id` FROM `ruoyi-vue-pro`.`system_users` WHERE `username` = 'admin' LIMIT 1)); -- ④ 超管账号 admin -- ④ admin 账号 DELETE FROM `system_users`; INSERT INTO `system_users` SELECT * FROM `ruoyi-vue-pro`.`system_users` WHERE `username` = 'admin' AND `deleted` = b'0'; -- ⑤ 用户-角色关联(admin 拥有的角色) -- ⑤ 用户-角色关联(admin) DELETE FROM `system_user_role`; INSERT INTO `system_user_role` SELECT * FROM `ruoyi-vue-pro`.`system_user_role` WHERE `user_id` = (SELECT `id` FROM `system_users` WHERE `username` = 'admin' LIMIT 1); -- ⑥ 角色-菜单关联(admin 各角色,保证普通角色权限正常) DELETE FROM `system_role_menu`; INSERT INTO `system_role_menu` SELECT rm.* FROM `ruoyi-vue-pro`.`system_role_menu` rm INNER JOIN `system_user_role` ur ON ur.`role_id` = rm.`role_id`; SET FOREIGN_KEY_CHECKS = 1; yudao-framework/yudao-spring-boot-starter-web/src/test/java/cn/iocoder/yudao/framework/desensitize/core/DesensitizeTest.java
@@ -42,7 +42,7 @@ DesensitizeDemo d = JsonUtils.parseObject(JsonUtils.toJsonString(desensitizeDemo), DesensitizeDemo.class); // 断言 assertNotNull(d); assertEquals("芋***", d.getNickname()); assertEquals("超****", d.getNickname()); assertEquals("998800********31", d.getBankCard()); assertEquals("粤A6***6", d.getCarLicense()); assertEquals("0108*****22", d.getFixedPhone()); yudao-module-bpm/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmUserGroupServiceTest.java
@@ -108,14 +108,14 @@ }); userGroupMapper.insert(dbUserGroup); // 测试 name 不匹配 userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setName("超级管理员"))); userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setName("其他用户组"))); // 测试 status 不匹配 userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); // 测试 createTime 不匹配 userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setCreateTime(buildTime(2021, 12, 12)))); // 准备参数 BpmUserGroupPageReqVO reqVO = new BpmUserGroupPageReqVO(); reqVO.setName("源码"); reqVO.setName("超级"); reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); reqVO.setCreateTime((new LocalDateTime[]{buildTime(2021, 11, 10),buildTime(2021, 11, 12)})); yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImplTest.java
@@ -223,7 +223,8 @@ String clientId = randomString(); String scope = randomString(); Boolean approved = randomBoolean(); LocalDateTime expireTime = LocalDateTime.ofInstant(randomDay(1, 30).toInstant(), ZoneId.systemDefault()); LocalDateTime expireTime = LocalDateTime.ofInstant(randomDay(1, 30).toInstant(), ZoneId.systemDefault()) .withNano(0); // mock 方法 // 调用 @@ -237,7 +238,7 @@ assertEquals(clientId, result.get(0).getClientId()); assertEquals(scope, result.get(0).getScope()); assertEquals(approved, result.get(0).getApproved()); assertEquals(expireTime, result.get(0).getExpiresTime()); assertEquals(expireTime.withNano(0), result.get(0).getExpiresTime()); } @Test @@ -251,7 +252,8 @@ String clientId = approve.getClientId(); String scope = approve.getScope(); Boolean approved = randomBoolean(); LocalDateTime expireTime = LocalDateTime.ofInstant(randomDay(1, 30).toInstant(), ZoneId.systemDefault()); LocalDateTime expireTime = LocalDateTime.ofInstant(randomDay(1, 30).toInstant(), ZoneId.systemDefault()) .withNano(0); // mock 方法 // 调用 @@ -266,7 +268,7 @@ assertEquals(clientId, result.get(0).getClientId()); assertEquals(scope, result.get(0).getScope()); assertEquals(approved, result.get(0).getApproved()); assertEquals(expireTime, result.get(0).getExpiresTime()); assertEquals(expireTime.withNano(0), result.get(0).getExpiresTime()); } } yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImplTest.java
@@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.system.service.oauth2; import cn.hutool.core.date.LocalDateTimeUtil; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.exception.ErrorCode; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -15,6 +14,7 @@ import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2AccessTokenMapper; import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2RefreshTokenMapper; import cn.iocoder.yudao.module.system.dal.redis.oauth2.OAuth2AccessTokenRedisDAO; import cn.iocoder.yudao.module.system.service.permission.PermissionService; import cn.iocoder.yudao.module.system.service.user.AdminUserService; import jakarta.annotation.Resource; import org.assertj.core.util.Lists; @@ -56,6 +56,8 @@ private OAuth2ClientService oauth2ClientService; @MockitoBean private AdminUserService adminUserService; @MockitoBean private PermissionService permissionService; @Test public void testCreateAccessToken() { @@ -309,9 +311,9 @@ // 测试 userType 不匹配 oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setUserType(2))); // 测试 userType 不匹配 oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setClientId("it_client"))); oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setClientId("other_client"))); // 测试 expireTime 不匹配 oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setExpiresTime(LocalDateTimeUtil.now()))); oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setExpiresTime(LocalDateTime.now().minusDays(1)))); // 准备参数 OAuth2AccessTokenPageReqVO reqVO = new OAuth2AccessTokenPageReqVO(); reqVO.setUserId(10L);