Commit 0281d6ed5b5d5190d416b724095a7b24e81bb9b2
1 parent
1ad70c5f
feat(usr): V2 种子超管 + USR 权限分类 REQ-USR-001
- V2 migration 写入 4 个 USR 权限分类 + admin 超管(BCrypt $2y$10$ 密码 'admin')+ admin × 4 授权 - docs/03 末尾追加「种子数据 (V2 migration)」小节同步 SSoT - application.yml JDBC URL 加 tinyInt1isBit=false 避免 tinyint(1) 误判为 Boolean
Showing
4 changed files
with
86 additions
and
1 deletions
backend/src/main/resources/application.yml
| @@ -6,7 +6,7 @@ spring: | @@ -6,7 +6,7 @@ spring: | ||
| 6 | config: | 6 | config: |
| 7 | import: optional:file:../.env.local[.properties] | 7 | import: optional:file:../.env.local[.properties] |
| 8 | datasource: | 8 | datasource: |
| 9 | - url: jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_SCHEMA}?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false | 9 | + url: jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_SCHEMA}?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false&tinyInt1isBit=false |
| 10 | username: ${DB_USER} | 10 | username: ${DB_USER} |
| 11 | password: ${DB_PASSWORD} | 11 | password: ${DB_PASSWORD} |
| 12 | driver-class-name: com.mysql.cj.jdbc.Driver | 12 | driver-class-name: com.mysql.cj.jdbc.Driver |
backend/src/test/java/com/xly/test4/ApplicationContextIT.java
| 1 | package com.xly.test4; | 1 | package com.xly.test4; |
| 2 | 2 | ||
| 3 | +import java.util.Map; | ||
| 4 | + | ||
| 3 | import org.junit.jupiter.api.Test; | 5 | import org.junit.jupiter.api.Test; |
| 4 | import org.springframework.beans.factory.annotation.Autowired; | 6 | import org.springframework.beans.factory.annotation.Autowired; |
| 5 | import org.springframework.boot.test.context.SpringBootTest; | 7 | import org.springframework.boot.test.context.SpringBootTest; |
| 6 | import org.springframework.context.ApplicationContext; | 8 | import org.springframework.context.ApplicationContext; |
| 9 | +import org.springframework.jdbc.core.JdbcTemplate; | ||
| 7 | 10 | ||
| 8 | import static org.assertj.core.api.Assertions.assertThat; | 11 | import static org.assertj.core.api.Assertions.assertThat; |
| 9 | 12 | ||
| @@ -13,9 +16,34 @@ class ApplicationContextIT { | @@ -13,9 +16,34 @@ class ApplicationContextIT { | ||
| 13 | @Autowired | 16 | @Autowired |
| 14 | private ApplicationContext context; | 17 | private ApplicationContext context; |
| 15 | 18 | ||
| 19 | + @Autowired | ||
| 20 | + private JdbcTemplate jdbcTemplate; | ||
| 21 | + | ||
| 16 | @Test | 22 | @Test |
| 17 | void contextLoads() { | 23 | void contextLoads() { |
| 18 | assertThat(context).isNotNull(); | 24 | assertThat(context).isNotNull(); |
| 19 | assertThat(context.getBean(Application.class)).isNotNull(); | 25 | assertThat(context.getBean(Application.class)).isNotNull(); |
| 20 | } | 26 | } |
| 27 | + | ||
| 28 | + @Test | ||
| 29 | + void flywayMigrationsApplied_seedDataPresent() { | ||
| 30 | + Integer permCount = jdbcTemplate.queryForObject( | ||
| 31 | + "SELECT COUNT(*) FROM tPermission WHERE sCategory IN " | ||
| 32 | + + "('usr:user:create','usr:user:update','usr:user:list','usr:user:assign-role')", | ||
| 33 | + Integer.class); | ||
| 34 | + assertThat(permCount).isEqualTo(4); | ||
| 35 | + | ||
| 36 | + Map<String, Object> admin = jdbcTemplate.queryForMap( | ||
| 37 | + "SELECT sUserType, sBrandsId, sSubsidiaryId, iIsDisabled FROM tUser WHERE sUserName = 'admin'"); | ||
| 38 | + assertThat(admin.get("sUserType")).isEqualTo("ADMIN"); | ||
| 39 | + assertThat(admin.get("sBrandsId")).isEqualTo("BR-DEFAULT"); | ||
| 40 | + assertThat(admin.get("sSubsidiaryId")).isEqualTo("SUB-DEFAULT"); | ||
| 41 | + assertThat(((Number) admin.get("iIsDisabled")).intValue()).isEqualTo(0); | ||
| 42 | + | ||
| 43 | + Integer authCount = jdbcTemplate.queryForObject( | ||
| 44 | + "SELECT COUNT(*) FROM tUserPermission WHERE iUserId = " | ||
| 45 | + + "(SELECT iIncrement FROM tUser WHERE sUserName = 'admin')", | ||
| 46 | + Integer.class); | ||
| 47 | + assertThat(authCount).isEqualTo(4); | ||
| 48 | + } | ||
| 21 | } | 49 | } |
docs/03-数据库设计文档.md
| @@ -229,3 +229,28 @@ tUser ──────────────────────── | @@ -229,3 +229,28 @@ tUser ──────────────────────── | ||
| 229 | - 字典表,提供 REQ-USR-004 登录页「版本」下拉数据。 | 229 | - 字典表,提供 REQ-USR-004 登录页「版本」下拉数据。 |
| 230 | - 不与 `tUser` 建外键:账号可在多公司 / 多版本之间复用;登录时仅用作上下文选择,由应用层校验"用户是否被允许进入该公司版本"。 | 230 | - 不与 `tUser` 建外键:账号可在多公司 / 多版本之间复用;登录时仅用作上下文选择,由应用层校验"用户是否被允许进入该公司版本"。 |
| 231 | - 当 `sEdition` 的取值集合稳定后,可改为受控字典并迁移到 `tDictionary` 类公共字典表;当前阶段独立维护。 | 231 | - 当 `sEdition` 的取值集合稳定后,可改为受控字典并迁移到 `tDictionary` 类公共字典表;当前阶段独立维护。 |
| 232 | + | ||
| 233 | +--- | ||
| 234 | + | ||
| 235 | +## 种子数据(V2 migration) | ||
| 236 | + | ||
| 237 | +由 `sql/migrations/V2__seed_admin_and_permissions.sql` 写入,REQ-USR-001 配套。 | ||
| 238 | + | ||
| 239 | +### `tPermission` | ||
| 240 | + | ||
| 241 | +| sCategory | sCategoryName | 用途 | | ||
| 242 | +|---|---|---| | ||
| 243 | +| `usr:user:create` | 新增用户 | REQ-USR-001 接口 `@PreAuthorize` | | ||
| 244 | +| `usr:user:update` | 修改用户 | REQ-USR-002 接口 `@PreAuthorize` | | ||
| 245 | +| `usr:user:list` | 查询用户 | REQ-USR-003 接口 `@PreAuthorize` | | ||
| 246 | +| `usr:user:assign-role` | 分配用户角色 | REQ-USR-002 修改 `userType` 子权限 | | ||
| 247 | + | ||
| 248 | +### `tUser` — 超级管理员 | ||
| 249 | + | ||
| 250 | +| sUserCode | sUserName | sUserType | sLanguage | sBrandsId | sSubsidiaryId | 明文密码 | | ||
| 251 | +|---|---|---|---|---|---|---| | ||
| 252 | +| ADMIN001 | admin | ADMIN | zh-CN | BR-DEFAULT | SUB-DEFAULT | `admin`(BCrypt $2y$10$ 哈希存入) | | ||
| 253 | + | ||
| 254 | +### `tUserPermission` | ||
| 255 | + | ||
| 256 | +admin × 上述 4 个权限分类,全部授权(`sGrantedBy='system'`)。 |
sql/migrations/V2__seed_admin_and_permissions.sql
0 → 100644
| 1 | +-- V2__seed_admin_and_permissions.sql | ||
| 2 | +-- REQ-USR-001 配套种子:USR 模块权限分类 + 超级管理员账号 | ||
| 3 | +-- | ||
| 4 | +-- 来源:docs/superpowers/specs/2026-05-13-REQ-USR-001.md § 附带基础设施 / Migration | ||
| 5 | +-- 密码哈希算法:BCrypt($2y$10$,等价于 $2a$10$,Spring Security BCryptPasswordEncoder 兼容) | ||
| 6 | +-- 明文密码:admin(仅本地开发/测试用,生产环境 admin 账号上线后必须立即改密) | ||
| 7 | + | ||
| 8 | +-- 种子权限分类(覆盖 USR 模块全部 REQ 所需) | ||
| 9 | +INSERT INTO tPermission (sCategory, sCategoryName, sDescription, iIsDisabled) | ||
| 10 | +VALUES | ||
| 11 | + ('usr:user:create', '新增用户', 'REQ-USR-001 增加用户', 0), | ||
| 12 | + ('usr:user:update', '修改用户', 'REQ-USR-002 修改用户', 0), | ||
| 13 | + ('usr:user:list', '查询用户', 'REQ-USR-003 查询用户', 0), | ||
| 14 | + ('usr:user:assign-role', '分配用户角色', '修改用户类型 NORMAL/ADMIN 用', 0); | ||
| 15 | + | ||
| 16 | +-- 种子超级管理员 | ||
| 17 | +INSERT INTO tUser | ||
| 18 | + (sUserCode, sUserName, iEmployeeId, sUserType, sLanguage, iCanEditDoc, | ||
| 19 | + sPasswordHash, iIsDisabled, iLoginFailCount, | ||
| 20 | + sBrandsId, sSubsidiaryId, sCreatedBy) | ||
| 21 | +VALUES | ||
| 22 | + ('ADMIN001', 'admin', NULL, 'ADMIN', 'zh-CN', 1, | ||
| 23 | + '$2y$10$Diskv9jfPWYxJGof9IazgeKj2CFCztxuxH0WQ1TDJRwlizKXi1h.2', 0, 0, | ||
| 24 | + 'BR-DEFAULT', 'SUB-DEFAULT', 'system'); | ||
| 25 | + | ||
| 26 | +-- 给 admin 授权全部 4 个权限分类 | ||
| 27 | +INSERT INTO tUserPermission (iUserId, iPermissionId, sGrantedBy, sBrandsId, sSubsidiaryId) | ||
| 28 | +SELECT u.iIncrement, p.iIncrement, 'system', 'BR-DEFAULT', 'SUB-DEFAULT' | ||
| 29 | +FROM tUser u | ||
| 30 | +CROSS JOIN tPermission p | ||
| 31 | +WHERE u.sUserName = 'admin' | ||
| 32 | + AND p.sCategory IN ('usr:user:create','usr:user:update','usr:user:list','usr:user:assign-role'); |