02_system_seed.sql 13.9 KB
-- ============================================================================
--  parking-cloud · P1 系统管理种子数据
--
--  执行前提:先执行 01_system_schema.sql
--  幂等:全部 INSERT IGNORE + 显式主键,可重复执行(不会重复插、不会覆盖已改过的数据)
--
--  ⚠️ 本脚本**只用于本地/开发环境初始化**。上线前必须处理两处:
--     1. admin 的初始密码(见 3. 的说明)
--     2. OAuth2 客户端的 secret(见 8. 的说明)
--     红线第 11 条:禁止把生产密钥提交进仓库。
--
--  菜单 id 是**刻意固定**的(1 / 2 / 21~26),与 P0 占位实现
--  P0AuthController#buildP0Menus 下发的 id 完全一致,这样 P1 换成真实现后,
--  前端的动态路由与已打开的标签页不会因为 id 变化而失配。
-- ============================================================================

USE parking_cloud;

-- ---------------------------------------------------------------------------
-- 1. 组织
-- ---------------------------------------------------------------------------
INSERT IGNORE INTO system_org (id, name, parent_id, sort, leader, mobile, email, status, remark, create_by, update_by)
VALUES (1, '总公司', 0, 1, '管理员', '', '', 0, '顶级组织(种子数据)', 'seed', 'seed');

-- ---------------------------------------------------------------------------
-- 2. 角色
--    ⚠️ 超级管理员的 **id 必须为 1** 且 **code 必须为 super_admin** —— 两处硬编码依赖它:
--      · 后端 PermissionServiceImpl 用 id=1 判定超管,超管直接拥有全部权限点
--      · 前端 store/modules/user.ts 的 isSuperAdmin 判定 roles.includes('super_admin')
--    改动这一行会让「超管恒有权限」这条链路静默失效(表现为按钮消失但没有报错)。
-- ---------------------------------------------------------------------------
INSERT IGNORE INTO system_role (id, name, code, sort, data_scope, status, type, remark, create_by, update_by)
VALUES (1, '超级管理员', 'super_admin', 1, 1, 0, 1, '内置角色:拥有全部权限,不可删除', 'seed', 'seed');

-- ---------------------------------------------------------------------------
-- 3. 用户
--    admin / admin123
--    password 是 BCrypt 密文,已用 Spring Security 5.7.12 的
--    BCryptPasswordEncoder#matches("admin123", <该串>) 实测为 true
--    (密文以 $2y$ 开头,Spring 的 BCrypt 接受 $2a$/$2b$/$2y$ 三种版本前缀)。
--    ⚠️ admin123 是弱口令,仅供本地联调;上线前必须改密(走 /system/user/update-password)。
-- ---------------------------------------------------------------------------
INSERT IGNORE INTO system_users (id, username, password, nickname, remark, org_id, email, mobile, sex, status,
                                 create_by, update_by)
VALUES (1, 'admin', '$2y$10$SG.m/uKIJpcEgJLnvPgZteLpU2PJ1Dv5EBp9krhSmBVASm45XSZ4C', '管理员',
        '种子数据:初始超级管理员', 1, '', '', 0, 0, 'seed', 'seed');

-- ---------------------------------------------------------------------------
-- 4. 用户 ↔ 角色
-- ---------------------------------------------------------------------------
INSERT IGNORE INTO system_user_role (user_id, role_id, create_by)
VALUES (1, 1, 'seed');

-- ---------------------------------------------------------------------------
-- 5. 菜单树
--    type:1 目录,2 菜单,3 按钮(按钮只承载权限标识,不进前端菜单树)
--    component:相对 src/views 的路径,必须是**真实存在**的 .vue ——
--      前端 store/modules/permission.ts 用 import.meta.glob('/src/views/**/*.vue') 解析,
--      填错不会报错,只会让该菜单点开后是空白页。
--   目录节点的 component 必须为 ''(前端只给有 component 的节点建路由),
--      但目录**必须有 path**(/system)作为子菜单路由的前缀。
-- ---------------------------------------------------------------------------

-- 5.1 顶级:首页 + 系统管理
INSERT IGNORE INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, create_by, update_by) VALUES
(1, '首页',     '', 2, 1, 0, '/dashboard', 'ep:home-filled', 'dashboard/index', 'Dashboard', 0, 1, 'seed', 'seed'),
(2, '系统管理', '', 1, 2, 0, '/system',    'ep:setting',     '',                '',          0, 1, 'seed', 'seed');

-- 5.2 一级菜单(系统管理下的 6 个页面)
INSERT IGNORE INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, create_by, update_by) VALUES
(21, '用户管理', '', 2, 1, 2, '/system/user', 'ep:user',            'system/user/index', 'SystemUser', 0, 1, 'seed', 'seed'),
(22, '角色管理', '', 2, 2, 2, '/system/role', 'ep:avatar',          'system/role/index', 'SystemRole', 0, 1, 'seed', 'seed'),
(23, '菜单管理', '', 2, 3, 2, '/system/menu', 'ep:menu',            'system/menu/index', 'SystemMenu', 0, 1, 'seed', 'seed'),
(24, '组织管理', '', 2, 4, 2, '/system/org',  'ep:office-building', 'system/org/index',  'SystemOrg',  0, 1, 'seed', 'seed'),
(25, '字典管理', '', 2, 5, 2, '/system/dict', 'ep:collection',      'system/dict/index', 'SystemDict', 0, 1, 'seed', 'seed'),
(26, '日志管理', '', 2, 6, 2, '/system/log',  'ep:document',        'system/log/index',  'SystemLog',  0, 1, 'seed', 'seed');

-- 5.3 按钮权限点
--     命名规范 模块:资源:动作,与 Controller 上
--     @PreAuthorize("@ss.hasPermission('...')") 以及前端 v-hasPermi="'...'" 三处必须逐字一致。
--     资源名与前端 API 文件的 BASE 路径对齐:标签用 dict,路径用 /system/dict-type。
INSERT IGNORE INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, create_by, update_by) VALUES
-- 用户管理
(101, '用户查询', 'system:user:query',           3, 1, 21, '', '', '', '', 0, 1, 'seed', 'seed'),
(102, '用户新增', 'system:user:create',          3, 2, 21, '', '', '', '', 0, 1, 'seed', 'seed'),
(103, '用户修改', 'system:user:update',          3, 3, 21, '', '', '', '', 0, 1, 'seed', 'seed'),
(104, '用户删除', 'system:user:delete',          3, 4, 21, '', '', '', '', 0, 1, 'seed', 'seed'),
(105, '用户导出', 'system:user:export',          3, 5, 21, '', '', '', '', 0, 1, 'seed', 'seed'),
(106, '重置密码', 'system:user:update-password', 3, 6, 21, '', '', '', '', 0, 1, 'seed', 'seed'),
-- 角色管理
(111, '角色查询', 'system:role:query',  3, 1, 22, '', '', '', '', 0, 1, 'seed', 'seed'),
(112, '角色新增', 'system:role:create', 3, 2, 22, '', '', '', '', 0, 1, 'seed', 'seed'),
(113, '角色修改', 'system:role:update', 3, 3, 22, '', '', '', '', 0, 1, 'seed', 'seed'),
(114, '角色删除', 'system:role:delete', 3, 4, 22, '', '', '', '', 0, 1, 'seed', 'seed'),
(115, '角色导出', 'system:role:export', 3, 5, 22, '', '', '', '', 0, 1, 'seed', 'seed'),
-- 菜单管理
(121, '菜单查询', 'system:menu:query',  3, 1, 23, '', '', '', '', 0, 1, 'seed', 'seed'),
(122, '菜单新增', 'system:menu:create', 3, 2, 23, '', '', '', '', 0, 1, 'seed', 'seed'),
(123, '菜单修改', 'system:menu:update', 3, 3, 23, '', '', '', '', 0, 1, 'seed', 'seed'),
(124, '菜单删除', 'system:menu:delete', 3, 4, 23, '', '', '', '', 0, 1, 'seed', 'seed'),
(125, '菜单导出', 'system:menu:export', 3, 5, 23, '', '', '', '', 0, 1, 'seed', 'seed'),
-- 组织管理
(131, '组织查询', 'system:org:query',  3, 1, 24, '', '', '', '', 0, 1, 'seed', 'seed'),
(132, '组织新增', 'system:org:create', 3, 2, 24, '', '', '', '', 0, 1, 'seed', 'seed'),
(133, '组织修改', 'system:org:update', 3, 3, 24, '', '', '', '', 0, 1, 'seed', 'seed'),
(134, '组织删除', 'system:org:delete', 3, 4, 24, '', '', '', '', 0, 1, 'seed', 'seed'),
(135, '组织导出', 'system:org:export', 3, 5, 24, '', '', '', '', 0, 1, 'seed', 'seed'),
-- 字典管理
(141, '字典查询', 'system:dict:query',  3, 1, 25, '', '', '', '', 0, 1, 'seed', 'seed'),
(142, '字典新增', 'system:dict:create', 3, 2, 25, '', '', '', '', 0, 1, 'seed', 'seed'),
(143, '字典修改', 'system:dict:update', 3, 3, 25, '', '', '', '', 0, 1, 'seed', 'seed'),
(144, '字典删除', 'system:dict:delete', 3, 4, 25, '', '', '', '', 0, 1, 'seed', 'seed'),
(145, '字典导出', 'system:dict:export', 3, 5, 25, '', '', '', '', 0, 1, 'seed', 'seed'),
-- 日志管理(操作日志只读,无 create/update/delete)
(151, '日志查询', 'system:operate-log:query',  3, 1, 26, '', '', '', '', 0, 1, 'seed', 'seed'),
(152, '日志导出', 'system:operate-log:export', 3, 2, 26, '', '', '', '', 0, 1, 'seed', 'seed');

-- ---------------------------------------------------------------------------
-- 6. 角色 ↔ 菜单:超管拿全部
--    用 SELECT 而不是罗列 id,这样以后加菜单不必回来改本脚本。
--    依赖 system_role_menu 的 uk_role_menu 唯一键做幂等去重。
-- ---------------------------------------------------------------------------
INSERT IGNORE INTO system_role_menu (role_id, menu_id, create_by)
SELECT 1, id, 'seed' FROM system_menu;

-- ---------------------------------------------------------------------------
-- 7. 字典
-- ---------------------------------------------------------------------------
INSERT IGNORE INTO system_dict_type (id, name, type, status, remark, create_by, update_by) VALUES
(1, '通用状态',   'system_common_status', 0, '0 开启 / 1 停用',              'seed', 'seed'),
(2, '用户性别',   'system_user_sex',      0, '',                             'seed', 'seed'),
(3, '菜单类型',   'system_menu_type',     0, '1 目录 / 2 菜单 / 3 按钮',      'seed', 'seed'),
(4, '数据范围',   'system_data_scope',    0, '角色可访问的数据范围',           'seed', 'seed'),
(5, '操作类型',   'system_operate_type',  0, '操作日志的操作类型',             'seed', 'seed');

INSERT IGNORE INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, create_by, update_by) VALUES
(1,  1, '开启',       '0', 'system_common_status', 0, 'success', '', '', 'seed', 'seed'),
(2,  2, '停用',       '1', 'system_common_status', 0, 'danger',  '', '', 'seed', 'seed'),
(11, 1, '未知',       '0', 'system_user_sex',      0, 'info',    '', '', 'seed', 'seed'),
(12, 2, '男',         '1', 'system_user_sex',      0, 'info',    '', '', 'seed', 'seed'),
(13, 3, '女',         '2', 'system_user_sex',      0, 'info',    '', '', 'seed', 'seed'),
(21, 1, '目录',       '1', 'system_menu_type',     0, 'info',    '', '', 'seed', 'seed'),
(22, 2, '菜单',       '2', 'system_menu_type',     0, 'info',    '', '', 'seed', 'seed'),
(23, 3, '按钮',       '3', 'system_menu_type',     0, 'info',    '', '', 'seed', 'seed'),
(31, 1, '全部',       '1', 'system_data_scope',    0, 'info',    '', '', 'seed', 'seed'),
(32, 2, '自定义',     '2', 'system_data_scope',    0, 'info',    '', '', 'seed', 'seed'),
(33, 3, '本组织',     '3', 'system_data_scope',    0, 'info',    '', '', 'seed', 'seed'),
(34, 4, '本组织及以下', '4', 'system_data_scope',  0, 'info',    '', '', 'seed', 'seed'),
(35, 5, '仅本人',     '5', 'system_data_scope',    0, 'info',    '', '', 'seed', 'seed'),
-- 操作类型的 value 直接就是中文,与 system_operate_log.type 列存的**同一个字符串**:
-- 前端日志页 v1 是直接打印 row.type(没有 DictTag),所以列里必须是可直接展示的值;
-- 这里仍保留成字典,是为了将来换成 DictTag 渲染时不用改数据。
(41, 1, '查询',       '查询', 'system_operate_type',  0, 'info',    '', '', 'seed', 'seed'),
(42, 2, '新增',       '新增', 'system_operate_type',  0, 'success', '', '', 'seed', 'seed'),
(43, 3, '修改',       '修改', 'system_operate_type',  0, 'warning', '', '', 'seed', 'seed'),
(44, 4, '删除',       '删除', 'system_operate_type',  0, 'danger',  '', '', 'seed', 'seed');

-- ---------------------------------------------------------------------------
-- 8. OAuth2 客户端
--    client_id 与 P0 占位实现保持一致(parking-cloud-ui),改动会让已签发的 token 对不上。
--
--    ⚠️ secret 这里填的是**本地占位值**,不是密钥:管理后台是本方 SPA,
--       浏览器里放任何 "客户端密钥" 都不构成秘密,所以登录接口(/system/auth/login)
--       只收 username/password,**不校验 client secret**。该列留给以后接入第三方
--       授权(authorization_code)时使用。生产环境请通过环境变量注入真实值。
-- ---------------------------------------------------------------------------
INSERT IGNORE INTO system_oauth2_client (id, client_id, secret, name, logo, description, status,
                                        access_token_validity_seconds, refresh_token_validity_seconds,
                                        redirect_uris, authorized_grant_types, scopes, auto_approve_scopes,
                                        additional_information, create_by, update_by)
VALUES (1, 'parking-cloud-ui', 'local-dev-placeholder-not-a-secret', '停车云管理后台', '', 'parking-cloud-ui 前端使用',
        0, 1800, 2592000, '', 'password,refresh_token', 'server', '', '', 'seed', 'seed');

-- ---------------------------------------------------------------------------
-- 校验
-- ---------------------------------------------------------------------------
SELECT '组织'   AS 表, COUNT(*) AS 行数 FROM system_org
UNION ALL SELECT '角色',        COUNT(*) FROM system_role
UNION ALL SELECT '用户',        COUNT(*) FROM system_users
UNION ALL SELECT '用户角色',    COUNT(*) FROM system_user_role
UNION ALL SELECT '菜单',        COUNT(*) FROM system_menu
UNION ALL SELECT '菜单(按钮)',  COUNT(*) FROM system_menu WHERE type = 3
UNION ALL SELECT '角色菜单',    COUNT(*) FROM system_role_menu
UNION ALL SELECT '字典类型',    COUNT(*) FROM system_dict_type
UNION ALL SELECT '字典数据',    COUNT(*) FROM system_dict_data
UNION ALL SELECT 'OAuth2客户端', COUNT(*) FROM system_oauth2_client;