""" QA 数据库初始化脚本 (幂等版本) 数据库: mokeegatewayqa / 用户: mokeegatewayqa / 密码: mokee. """ import pymysql DB = { 'host': 'db1.qa.zgitm.com', 'port': 3306, 'user': 'mokeegatewayqa', 'password': 'mokee.', 'database': 'mokeegatewayqa', 'charset': 'utf8mb4', } DDL = [ # 1 """CREATE TABLE IF NOT EXISTS sys_system ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', system_code VARCHAR(64) NOT NULL, system_name VARCHAR(128) NOT NULL, front_url VARCHAR(256) NOT NULL, backend_url VARCHAR(256) NOT NULL, gateway_url VARCHAR(256) NOT NULL, icon VARCHAR(512) DEFAULT NULL, sort_order INT DEFAULT 0, status TINYINT NOT NULL DEFAULT 1, description VARCHAR(512) DEFAULT NULL, doc_token VARCHAR(128) DEFAULT NULL, created_by BIGINT DEFAULT NULL, updated_by BIGINT DEFAULT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, is_deleted TINYINT NOT NULL DEFAULT 0, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='业务系统表';""", # 2 """CREATE TABLE IF NOT EXISTS sys_api ( id BIGINT NOT NULL AUTO_INCREMENT, system_id BIGINT NOT NULL, api_path VARCHAR(256) NOT NULL, api_method VARCHAR(10) NOT NULL, api_name VARCHAR(128) DEFAULT NULL, api_desc VARCHAR(512) DEFAULT NULL, status TINYINT NOT NULL DEFAULT 1, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_system_id (system_id), UNIQUE KEY uk_system_path_method (system_id, api_path, api_method) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统放行接口表';""", # 3 """CREATE TABLE IF NOT EXISTS sys_user ( id BIGINT NOT NULL AUTO_INCREMENT, username VARCHAR(64) NOT NULL, password VARCHAR(256) NOT NULL, real_name VARCHAR(64) DEFAULT NULL, email VARCHAR(128) DEFAULT NULL, phone VARCHAR(32) DEFAULT NULL, avatar VARCHAR(512) DEFAULT NULL, post VARCHAR(64) DEFAULT NULL, status TINYINT NOT NULL DEFAULT 1, lock_until DATETIME DEFAULT NULL, login_fail_count INT NOT NULL DEFAULT 0, last_login_ip VARCHAR(64) DEFAULT NULL, last_login_time DATETIME DEFAULT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, is_deleted TINYINT NOT NULL DEFAULT 0, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';""", # 4 """CREATE TABLE IF NOT EXISTS sys_user_system ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL, system_id BIGINT NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_user_system (user_id, system_id), KEY idx_system_id (system_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户系统绑定表';""", # 5 """CREATE TABLE IF NOT EXISTS sys_role ( id BIGINT NOT NULL AUTO_INCREMENT, system_id BIGINT NOT NULL, role_name VARCHAR(64) NOT NULL, role_code VARCHAR(64) NOT NULL, description VARCHAR(256) DEFAULT NULL, status TINYINT NOT NULL DEFAULT 1, created_by BIGINT DEFAULT NULL, updated_by BIGINT DEFAULT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, is_deleted TINYINT NOT NULL DEFAULT 0, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色表';""", # 6 """CREATE TABLE IF NOT EXISTS sys_user_role ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL, role_id BIGINT NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_user_role (user_id, role_id), KEY idx_role_id (role_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户角色表';""", # 7 """CREATE TABLE IF NOT EXISTS sys_menu ( id BIGINT NOT NULL AUTO_INCREMENT, system_id BIGINT NOT NULL, parent_id BIGINT NOT NULL DEFAULT 0, menu_name VARCHAR(64) NOT NULL, menu_path VARCHAR(256) DEFAULT NULL, component VARCHAR(256) DEFAULT NULL, icon VARCHAR(128) DEFAULT NULL, menu_type TINYINT NOT NULL, permission VARCHAR(128) DEFAULT NULL, sort_order INT NOT NULL DEFAULT 0, status TINYINT NOT NULL DEFAULT 1, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_system_id (system_id), KEY idx_parent_id (parent_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='菜单表';""", # 8 """CREATE TABLE IF NOT EXISTS sys_role_menu ( id BIGINT NOT NULL AUTO_INCREMENT, role_id BIGINT NOT NULL, menu_id BIGINT NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_role_menu (role_id, menu_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色菜单表';""", # 9 """CREATE TABLE IF NOT EXISTS sys_login_log ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT DEFAULT NULL, username VARCHAR(64) NOT NULL, system_id BIGINT DEFAULT NULL, login_ip VARCHAR(64) DEFAULT NULL, user_agent VARCHAR(512) DEFAULT NULL, browser VARCHAR(64) DEFAULT NULL, os VARCHAR(64) DEFAULT NULL, status TINYINT NOT NULL, fail_reason VARCHAR(256) DEFAULT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_created_at (created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='登录日志表';""", # 10 """CREATE TABLE IF NOT EXISTS sys_operation_log ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT DEFAULT NULL, username VARCHAR(64) DEFAULT NULL, system_id BIGINT DEFAULT NULL, module VARCHAR(64) DEFAULT NULL, action VARCHAR(32) DEFAULT NULL, request_url VARCHAR(256) DEFAULT NULL, request_method VARCHAR(10) DEFAULT NULL, request_body TEXT DEFAULT NULL, response_status INT DEFAULT NULL, error_msg TEXT DEFAULT NULL, cost_time INT DEFAULT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_created_at (created_at), KEY idx_system_id (system_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='操作日志表';""", # 11 """CREATE TABLE IF NOT EXISTS sys_dict ( id BIGINT NOT NULL AUTO_INCREMENT, dict_type VARCHAR(64) NOT NULL, dict_label VARCHAR(128) NOT NULL, dict_value VARCHAR(128) NOT NULL, sort_order INT DEFAULT 0, status TINYINT NOT NULL DEFAULT 1, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_type_value (dict_type, dict_value) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='数据字典表';""", # 12 """CREATE TABLE IF NOT EXISTS sys_file ( id VARCHAR(64) NOT NULL COMMENT 'UUID主键', system_id VARCHAR(64) DEFAULT NULL COMMENT '所属系统ID', file_name VARCHAR(256) NOT NULL COMMENT '原始文件名', file_type VARCHAR(64) DEFAULT 'other' COMMENT '文件类型(icon/avatar/doc/other)', file_size BIGINT DEFAULT 0 COMMENT '文件大小(字节)', mime_type VARCHAR(128) DEFAULT NULL COMMENT 'MIME类型', md5 VARCHAR(64) DEFAULT NULL COMMENT '文件MD5摘要', upload_user_id VARCHAR(64) DEFAULT NULL COMMENT '上传用户ID', file_data LONGBLOB COMMENT '文件二进制数据', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '上传时间', PRIMARY KEY (id), INDEX idx_system_id (system_id), INDEX idx_file_type (file_type), INDEX idx_upload_user_id (upload_user_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='文件表';""", ] def init(): conn = pymysql.connect(**DB) cur = conn.cursor() print(f"已连接: {DB['host']}:{DB['port']}/{DB['database']}") # ===== 阶段0: 清空所有数据(按外键顺序) ===== print("\n===== 阶段0: 清空旧数据 =====") truncate_order = [ 'sys_operation_log', 'sys_login_log', 'sys_role_menu', 'sys_user_role', 'sys_user_system', 'sys_api', 'sys_menu', 'sys_role', 'sys_dict', 'sys_file', 'sys_user', 'sys_system', ] for tbl in truncate_order: cur.execute(f"DELETE FROM {tbl}") conn.commit() # 重置自增 for tbl in truncate_order: cur.execute(f"ALTER TABLE {tbl} AUTO_INCREMENT = 1") conn.commit() print(" 所有表已清空并重置自增") # ===== 阶段1: 建表 ===== print("\n===== 阶段1: 建表 =====") for ddl in DDL: tbl = ddl.split("TABLE IF NOT EXISTS")[1].strip().split("(")[0].strip().strip('`') cur.execute(ddl.strip()) conn.commit() print(f" OK {tbl}") print(" 建表完成") # ===== 阶段2: 插入管理员 ===== print("\n===== 阶段2: 管理员用户 =====") cur.execute("""INSERT INTO sys_user (username, password, real_name, email, phone, post, status) VALUES ('admin', '$2b$10$ffAZHF6lmUuNV6OJITADXeduD5mMTS0.7kElPI7zno2zGn/RF5uC6', '管理员', 'admin@mokee.com', '13800000000', '管理员', 1)""") conn.commit() cur.execute("SELECT id FROM sys_user WHERE username='admin'") admin_uid = cur.fetchone()[0] print(f" 管理员: id={admin_uid}") # ===== 阶段3: 测试系统 ===== print("\n===== 阶段3: 测试系统 (test) =====") cur.execute("""INSERT INTO sys_system (system_code, system_name, front_url, backend_url, gateway_url, status, description) VALUES ('test', '测试系统', 'http://127.0.0.1:5001', 'http://127.0.0.1:32000', 'http://127.0.0.1:9000/test', 1, '测试用业务系统')""") conn.commit() test_sid = cur.lastrowid print(f" 系统: id={test_sid}") # 绑定管理员 cur.execute("INSERT INTO sys_user_system (user_id, system_id) VALUES (%s, %s)", (admin_uid, test_sid)) conn.commit() # 测试系统角色 cur.execute("INSERT INTO sys_role (system_id, role_name, role_code, description, status) VALUES (%s, '管理员', 'admin', '系统管理员', 1)", (test_sid,)) cur.execute("INSERT INTO sys_role (system_id, role_name, role_code, description, status) VALUES (%s, '普通用户', 'user', '普通用户', 1)", (test_sid,)) conn.commit() cur.execute("SELECT id FROM sys_role WHERE role_code='admin' AND system_id=%s", (test_sid,)) test_admin_role = cur.fetchone()[0] # 测试系统菜单 cur.execute("INSERT INTO sys_menu (system_id, parent_id, menu_name, menu_path, component, icon, menu_type, sort_order, status) VALUES (%s, 0, '系统管理', '/system', 'Layout', 'Setting', 1, 1, 1)", (test_sid,)) cur.execute("INSERT INTO sys_menu (system_id, parent_id, menu_name, menu_path, component, icon, menu_type, sort_order, status) VALUES (%s, 0, '业务管理', '/biz', 'Layout', 'Document', 1, 2, 1)", (test_sid,)) conn.commit() cur.execute("SELECT id FROM sys_menu WHERE menu_name='系统管理' AND system_id=%s", (test_sid,)) sys_pid = cur.fetchone()[0] cur.execute("SELECT id FROM sys_menu WHERE menu_name='业务管理' AND system_id=%s", (test_sid,)) biz_pid = cur.fetchone()[0] test_subs = [ ('用户管理', '/system/user', 'system/user/index', 'User', 2, 1, sys_pid), ('角色管理', '/system/role', 'system/role/index', 'Avatar', 2, 2, sys_pid), ('订单管理', '/biz/order', 'biz/order/index', 'Tickets', 2, 1, biz_pid), ('报表查询', '/biz/report', 'biz/report/index', 'TrendCharts', 2, 2, biz_pid), ] for nm, path, comp, icon, mtype, sort, pid in test_subs: cur.execute("INSERT INTO sys_menu (system_id, parent_id, menu_name, menu_path, component, icon, menu_type, sort_order, status) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, 1)", (test_sid, pid, nm, path, comp, icon, mtype, sort)) conn.commit() # 管理员角色绑定所有菜单 cur.execute("INSERT INTO sys_role_menu (role_id, menu_id) SELECT %s, m.id FROM sys_menu m WHERE m.system_id=%s", (test_admin_role, test_sid)) conn.commit() # 分配角色 cur.execute("INSERT INTO sys_user_role (user_id, role_id) VALUES (%s, %s)", (admin_uid, test_admin_role)) conn.commit() # 测试系统放行接口 for method, path in [('GET', '/zgapi/v1/admin/**'), ('POST', '/zgapi/v1/admin/**'), ('PUT', '/zgapi/v1/admin/**'), ('DELETE', '/zgapi/v1/admin/**'), ('GET', '/zgapi/v1/auth/**'), ('POST', '/zgapi/v1/auth/**'), ('PUT', '/zgapi/v1/auth/**')]: cur.execute("INSERT INTO sys_api (system_id, api_path, api_method, api_name, status) VALUES (%s, %s, %s, '放行接口', 1)", (test_sid, path, method)) conn.commit() print(f" 测试系统完成 (1管理员 + 2角色 + 6菜单 + 7接口)") # ===== 阶段4: Gateway 系统 ===== print("\n===== 阶段4: 网关管理平台 (gateway) =====") cur.execute("""INSERT INTO sys_system (system_code, system_name, front_url, backend_url, gateway_url, icon, sort_order, status, description, doc_token) VALUES ('gateway', '网关管理平台', 'http://127.0.0.1:5001', 'http://127.0.0.1:9000', 'http://127.0.0.1:9000/gateway', '', 0, 1, '统一登录网关管理后台', 'gateway1234567890')""") conn.commit() gw_sid = cur.lastrowid print(f" 系统: id={gw_sid}") # 绑定管理员 cur.execute("INSERT INTO sys_user_system (user_id, system_id) VALUES (%s, %s)", (admin_uid, gw_sid)) conn.commit() # 角色 cur.execute("INSERT INTO sys_role (system_id, role_name, role_code, description, status) VALUES (%s, '超级管理员', 'super_admin', '网关平台超级管理员', 1)", (gw_sid,)) cur.execute("INSERT INTO sys_role (system_id, role_name, role_code, description, status) VALUES (%s, '普通管理员', 'admin', '网关平台普通管理员', 1)", (gw_sid,)) conn.commit() cur.execute("SELECT id FROM sys_role WHERE role_code='super_admin' AND system_id=%s", (gw_sid,)) gw_super_admin = cur.fetchone()[0] cur.execute("SELECT id FROM sys_role WHERE role_code='admin' AND system_id=%s", (gw_sid,)) gw_admin = cur.fetchone()[0] # 分配 super_admin cur.execute("INSERT INTO sys_user_role (user_id, role_id) VALUES (%s, %s)", (admin_uid, gw_super_admin)) conn.commit() # 顶级菜单 for name, path, comp, icon, mtype, sort in [ ('首页', '/admin/dashboard', '', 'HomeFilled', 1, 1), ('系统管理', None, '', 'Monitor', 1, 2), ('用户权限', None, '', 'User', 1, 3), ('日志审计', None, '', 'Notebook', 1, 4), ]: cur.execute("INSERT INTO sys_menu (system_id, parent_id, menu_name, menu_path, component, icon, menu_type, sort_order, status) VALUES (%s, 0, %s, %s, %s, %s, %s, %s, 1)", (gw_sid, name, path, comp, icon, mtype, sort)) conn.commit() def get_menu_id(name, sid): cur.execute("SELECT id FROM sys_menu WHERE menu_name=%s AND system_id=%s ORDER BY id LIMIT 1", (name, sid)) r = cur.fetchone() return r[0] if r else None gw_sys_pid = get_menu_id('系统管理', gw_sid) gw_user_pid = get_menu_id('用户权限', gw_sid) gw_log_pid = get_menu_id('日志审计', gw_sid) for name, path, comp, icon, mtype, sort, pid in [ ('系统列表', '/admin/system', '', 'List', 2, 1, gw_sys_pid), ('接口管理', '/admin/api', '', 'Link', 2, 2, gw_sys_pid), ('数据字典', '/admin/dict', '', 'Collection', 2, 3, gw_sys_pid), ('用户管理', '/admin/user', '', 'UserFilled', 2, 1, gw_user_pid), ('角色管理', '/admin/role', '', 'Avatar', 2, 2, gw_user_pid), ('菜单管理', '/admin/menu', '', 'Menu', 2, 3, gw_user_pid), ('登录日志', '/admin/log/login', '', 'Document', 2, 1, gw_log_pid), ('操作日志', '/admin/log/operation', '', 'Tickets', 2, 2, gw_log_pid), ]: cur.execute("INSERT INTO sys_menu (system_id, parent_id, menu_name, menu_path, component, icon, menu_type, sort_order, status) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, 1)", (gw_sid, pid, name, path, comp, icon, mtype, sort)) conn.commit() # 角色绑定所有菜单 cur.execute("INSERT INTO sys_role_menu (role_id, menu_id) SELECT %s, m.id FROM sys_menu m WHERE m.system_id=%s", (gw_super_admin, gw_sid)) cur.execute("INSERT INTO sys_role_menu (role_id, menu_id) SELECT %s, m.id FROM sys_menu m WHERE m.system_id=%s", (gw_admin, gw_sid)) conn.commit() # 放行接口 for path, method in [('/zgapi/v1/admin/**', 'GET'), ('/zgapi/v1/admin/**', 'POST'), ('/zgapi/v1/admin/**', 'PUT'), ('/zgapi/v1/admin/**', 'DELETE'), ('/zgapi/v1/auth/**', 'GET'), ('/zgapi/v1/auth/**', 'POST'), ('/zgapi/v1/auth/**', 'PUT')]: cur.execute("INSERT INTO sys_api (system_id, api_path, api_method, api_name, status) VALUES (%s, %s, %s, '放行接口', 1)", (gw_sid, path, method)) conn.commit() print(f" Gateway系统完成 (2角色 + 12菜单 + 7接口)") # ===== 验证 ===== print("\n" + "=" * 60) print(" 验 证 结 果") print("=" * 60) stats = [ ("系统数", "sys_system"), ("用户数", "sys_user"), ("菜单数", "sys_menu"), ("角色数", "sys_role"), ("角色菜单绑定", "sys_role_menu"), ("放行接口", "sys_api"), ("用户系统绑定", "sys_user_system"), ("用户角色绑定", "sys_user_role"), ] for label, tbl in stats: cur.execute(f"SELECT COUNT(*) FROM {tbl}") print(f" {label}: {cur.fetchone()[0]}") print("\n--- 系统列表 ---") cur.execute("SELECT id, system_code, system_name FROM sys_system") for r in cur.fetchall(): print(f" id={r[0]} {r[1]:10s} {r[2]}") print("\n--- 角色列表 ---") cur.execute("SELECT r.id, s.system_code, r.role_name, r.role_code FROM sys_role r JOIN sys_system s ON r.system_id=s.id") for r in cur.fetchall(): print(f" id={r[0]} {r[1]:10s} {r[2]:12s} ({r[3]})") print("\n--- Gateway 菜单 ---") cur.execute("SELECT id, parent_id, menu_name, menu_path, menu_type FROM sys_menu WHERE system_id=%s ORDER BY sort_order, id", (gw_sid,)) for r in cur.fetchall(): print(f" id={r[0]:2d} pid={r[1]:2d} {r[2]:8s} path={(r[3] or ''):25s} type={r[4]}") print("\n" + "=" * 60) print("QA 数据库初始化完成!") print(f"管理员账号: admin / admin123") print("=" * 60) cur.close() conn.close() if __name__ == '__main__': init()