Commit 0281d6ed5b5d5190d416b724095a7b24e81bb9b2

Authored by zichun
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
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');