diff --git a/shierfeng-fastapi-backend/sql/shierfeng-fastapi-pg.sql b/shierfeng-fastapi-backend/sql/shierfeng-fastapi-pg.sql index 406386f..7c9dd66 100644 --- a/shierfeng-fastapi-backend/sql/shierfeng-fastapi-pg.sql +++ b/shierfeng-fastapi-backend/sql/shierfeng-fastapi-pg.sql @@ -264,9 +264,9 @@ comment on table sys_menu is '菜单权限表'; -- 初始化-菜单信息表数据 -- ---------------------------- -- 一级菜单 -insert into sys_menu values(1, '系统管理', 0, '1', 'system', null, '', '', 1, 0, 'M', '0', '0', '', 'system', 'admin', current_timestamp, '', null, '系统管理目录'); -insert into sys_menu values(2, '系统监控', 0, '2', 'monitor', null, '', '', 1, 0, 'M', '0', '0', '', 'monitor', 'admin', current_timestamp, '', null, '系统监控目录'); -insert into sys_menu values(3, '系统工具', 0, '3', 'tool', null, '', '', 1, 0, 'M', '0', '0', '', 'tool', 'admin', current_timestamp, '', null, '系统工具目录'); +insert into sys_menu values(1, '系统管理', 0, '4', 'system', null, '', '', 1, 0, 'M', '0', '0', '', 'system', 'admin', current_timestamp, '', null, '系统管理目录'); +insert into sys_menu values(2, '系统监控', 0, '5', 'monitor', null, '', '', 1, 0, 'M', '0', '0', '', 'monitor', 'admin', current_timestamp, '', null, '系统监控目录'); +insert into sys_menu values(3, '系统工具', 0, '6', 'tool', null, '', '', 1, 0, 'M', '0', '0', '', 'tool', 'admin', current_timestamp, '', null, '系统工具目录'); -- 二级菜单 insert into sys_menu values(100, '用户管理', 1, '1', 'user', 'system/user/index', '', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 'admin', current_timestamp, '', null, '用户管理菜单'); insert into sys_menu values(101, '角色管理', 1, '2', 'role', 'system/role/index', '', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 'admin', current_timestamp, '', null, '角色管理菜单'); @@ -1446,12 +1446,12 @@ create table biz_pallet_inventory ( ); -- ---------------------------- --- 初始化-托盘业务菜单和权限 +-- 初始化-托盘业务一级菜单和权限 -- ---------------------------- insert into sys_menu values -(121, '基础资料维护', 1, '10', 'businessMaster', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', current_timestamp, '', null, ''), -(122, '业务管理', 1, '11', 'businessOperation', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', current_timestamp, '', null, ''), -(123, '报表查询', 1, '12', 'businessReport', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', current_timestamp, '', null, ''), +(121, '基础资料维护', 0, '1', 'businessMaster', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', current_timestamp, '', null, ''), +(122, '业务管理', 0, '2', 'businessOperation', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', current_timestamp, '', null, ''), +(123, '报表查询', 0, '3', 'businessReport', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', current_timestamp, '', null, ''), (502, '菌种资料', 121, '1', 'strain', 'master/strain/index', '', '', 1, 0, 'C', '0', '0', 'master:strain:list', '#', 'admin', current_timestamp, '', null, ''), (503, '客户资料', 121, '2', 'customer', 'master/customer/index', '', '', 1, 0, 'C', '0', '0', 'master:customer:list', '#', 'admin', current_timestamp, '', null, ''), (504, '客户现场', 121, '3', 'site', 'master/site/index', '', '', 1, 0, 'C', '0', '0', 'master:site:list', '#', 'admin', current_timestamp, '', null, ''), diff --git a/shierfeng-fastapi-backend/sql/shierfeng-fastapi.sql b/shierfeng-fastapi-backend/sql/shierfeng-fastapi.sql index 9ef681d..3ca0995 100644 --- a/shierfeng-fastapi-backend/sql/shierfeng-fastapi.sql +++ b/shierfeng-fastapi-backend/sql/shierfeng-fastapi.sql @@ -181,9 +181,9 @@ create table sys_menu ( -- 初始化-菜单信息表数据 -- ---------------------------- -- 一级菜单 -insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, '', '', 1, 0, 'M', '0', '0', '', 'system', 'admin', sysdate(), '', null, '系统管理目录'); -insert into sys_menu values('2', '系统监控', '0', '2', 'monitor', null, '', '', 1, 0, 'M', '0', '0', '', 'monitor', 'admin', sysdate(), '', null, '系统监控目录'); -insert into sys_menu values('3', '系统工具', '0', '3', 'tool', null, '', '', 1, 0, 'M', '0', '0', '', 'tool', 'admin', sysdate(), '', null, '系统工具目录'); +insert into sys_menu values('1', '系统管理', '0', '4', 'system', null, '', '', 1, 0, 'M', '0', '0', '', 'system', 'admin', sysdate(), '', null, '系统管理目录'); +insert into sys_menu values('2', '系统监控', '0', '5', 'monitor', null, '', '', 1, 0, 'M', '0', '0', '', 'monitor', 'admin', sysdate(), '', null, '系统监控目录'); +insert into sys_menu values('3', '系统工具', '0', '6', 'tool', null, '', '', 1, 0, 'M', '0', '0', '', 'tool', 'admin', sysdate(), '', null, '系统工具目录'); -- 二级菜单 insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', '', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 'admin', sysdate(), '', null, '用户管理菜单'); insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', '', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 'admin', sysdate(), '', null, '角色管理菜单'); @@ -282,10 +282,10 @@ insert into sys_menu values('1058', '导入代码', '116', '4', '#', '', '', '', insert into sys_menu values('1059', '预览代码', '116', '5', '#', '', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview', '#', 'admin', sysdate(), '', null, ''); insert into sys_menu values('1060', '生成代码', '116', '6', '#', '', '', '', 1, 0, 'F', '0', '0', 'tool:gen:code', '#', 'admin', sysdate(), '', null, ''); --- 托盘业务目录 -insert into sys_menu values('121', '基础资料维护', '1', '10', 'businessMaster', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', sysdate(), '', null, '基础资料维护目录'); -insert into sys_menu values('122', '业务管理', '1', '11', 'businessOperation', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', sysdate(), '', null, '业务管理目录'); -insert into sys_menu values('123', '报表查询', '1', '12', 'businessReport', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', sysdate(), '', null, '报表查询目录'); +-- 托盘业务一级目录 +insert into sys_menu values('121', '基础资料维护', '0', '1', 'businessMaster', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', sysdate(), '', null, '基础资料维护目录'); +insert into sys_menu values('122', '业务管理', '0', '2', 'businessOperation', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', sysdate(), '', null, '业务管理目录'); +insert into sys_menu values('123', '报表查询', '0', '3', 'businessReport', null, '', '', 1, 0, 'M', '0', '0', '', '#', 'admin', sysdate(), '', null, '报表查询目录'); -- 托盘业务页面 insert into sys_menu values('502', '菌种资料', '121', '1', 'strain', 'master/strain/index', '', '', 1, 0, 'C', '0', '0', 'master:strain:list', '#', 'admin', sysdate(), '', null, '菌种资料菜单'); diff --git a/shierfeng-fastapi-backend/tests/module_business/test_sql_contract.py b/shierfeng-fastapi-backend/tests/module_business/test_sql_contract.py index c18dcbf..bca6d2e 100644 --- a/shierfeng-fastapi-backend/tests/module_business/test_sql_contract.py +++ b/shierfeng-fastapi-backend/tests/module_business/test_sql_contract.py @@ -6,6 +6,7 @@ from pathlib import Path SQL_ROOT = Path(__file__).resolve().parents[2] / 'sql' INITIALIZERS = ('shierfeng-fastapi.sql', 'shierfeng-fastapi-pg.sql') PATCH_PATH = SQL_ROOT / 'patches/20260724_pallet_workflow_mysql.sql' +MENU_PROMOTION_PATCH_PATH = SQL_ROOT / 'patches/20260727_promote_business_menus_mysql.sql' SYS_MENU_VALUE_COUNT = 20 SYS_MENU_PERMS_INDEX = 13 REMOVED_AI_DICT_CODES = set(range(33, 70)) @@ -492,9 +493,9 @@ EXPECTED_BUTTON_PERMISSIONS = frozenset( ) EXPECTED_PERMISSIONS = EXPECTED_PAGE_PERMISSIONS | EXPECTED_BUTTON_PERMISSIONS BUSINESS_MENU_SPECS = """ -121|1|基础资料维护|businessMaster||10|| -122|1|业务管理|businessOperation||11|| -123|1|报表查询|businessReport||12|| +121|0|基础资料维护|businessMaster||1|| +122|0|业务管理|businessOperation||2|| +123|0|报表查询|businessReport||3|| 502|121|菌种资料|strain||1|master:strain:list|master/strain/index 503|121|客户资料|customer||2|master:customer:list|master/customer/index 504|121|客户现场|site||3|master:site:list|master/site/index @@ -576,6 +577,20 @@ def _expected_business_menu_fields() -> dict[int, tuple]: EXPECTED_BUSINESS_MENU_FIELDS = _expected_business_menu_fields() +LEGACY_PATCH_BUSINESS_MENU_FIELDS = EXPECTED_BUSINESS_MENU_FIELDS | { + 121: (1, '基础资料维护', 'businessMaster', '', 10, '', None), + 122: (1, '业务管理', 'businessOperation', '', 11, '', None), + 123: (1, '报表查询', 'businessReport', '', 12, '', None), +} +EXPECTED_TOP_LEVEL_MENU_POSITIONS = { + 121: ('基础资料维护', 0, 1), + 122: ('业务管理', 0, 2), + 123: ('报表查询', 0, 3), + 1: ('系统管理', 0, 4), + 2: ('系统监控', 0, 5), + 3: ('系统工具', 0, 6), +} +MENU_PROMOTION_IDS = set(EXPECTED_TOP_LEVEL_MENU_POSITIONS) BUSINESS_MENU_IDS = set(range(121, 124)) | set(range(502, 519)) | set(range(1065, 1109)) MYSQL_MENU_STABLE_COLUMNS = ( 'menu_name', @@ -842,16 +857,24 @@ def _is_postgres_naive_timestamp_with_default_precision(definition: str) -> bool ) -def _business_menu_rows(sql: str) -> dict[int, list]: +def _menu_rows(sql: str, menu_ids: set[int]) -> dict[int, list]: rows = _insert_rows(sql, 'sys_menu') - selected = [row for row in rows if row and str(row[0]).isdigit() and int(row[0]) in BUSINESS_MENU_IDS] - counts = {menu_id: sum(int(row[0]) == menu_id for row in selected) for menu_id in BUSINESS_MENU_IDS} - if set(counts) != BUSINESS_MENU_IDS or any(count != 1 for count in counts.values()): - raise AssertionError(f'business menu row counts differ: {counts}') + selected = [row for row in rows if row and str(row[0]).isdigit() and int(row[0]) in menu_ids] + counts = {menu_id: sum(int(row[0]) == menu_id for row in selected) for menu_id in menu_ids} + if set(counts) != menu_ids or any(count != 1 for count in counts.values()): + raise AssertionError(f'menu row counts differ: {counts}') return {int(row[0]): row for row in selected} -def _assert_business_menu_fields(test_case: unittest.TestCase, sql: str) -> None: +def _business_menu_rows(sql: str) -> dict[int, list]: + return _menu_rows(sql, BUSINESS_MENU_IDS) + + +def _assert_business_menu_fields( + test_case: unittest.TestCase, + sql: str, + expected_fields: dict[int, tuple] = EXPECTED_BUSINESS_MENU_FIELDS, +) -> None: rows = _business_menu_rows(sql) invalid_widths = {menu_id: len(row) for menu_id, row in rows.items() if len(row) != SYS_MENU_VALUE_COUNT} test_case.assertEqual({}, invalid_widths) @@ -867,7 +890,7 @@ def _assert_business_menu_fields(test_case: unittest.TestCase, sql: str) -> None ) for menu_id, row in rows.items() } - test_case.assertEqual(EXPECTED_BUSINESS_MENU_FIELDS, actual) + test_case.assertEqual(expected_fields, actual) def _assert_business_permission_inventory(test_case: unittest.TestCase, sql: str) -> None: @@ -944,15 +967,15 @@ class SqlContractTest(unittest.TestCase): rows = _business_menu_rows(sql) self.assertTrue(all(len(row) == SYS_MENU_VALUE_COUNT for row in rows.values())) self.assertEqual( - ('基础资料维护', '1', '10', 'businessMaster', 'M', ''), + ('基础资料维护', '0', '1', 'businessMaster', 'M', ''), tuple(rows[121][index] for index in (1, 2, 3, 4, 10, 13)), ) self.assertEqual( - ('业务管理', '1', '11', 'businessOperation', 'M', ''), + ('业务管理', '0', '2', 'businessOperation', 'M', ''), tuple(rows[122][index] for index in (1, 2, 3, 4, 10, 13)), ) self.assertEqual( - ('报表查询', '1', '12', 'businessReport', 'M', ''), + ('报表查询', '0', '3', 'businessReport', 'M', ''), tuple(rows[123][index] for index in (1, 2, 3, 4, 10, 13)), ) for menu_id in (121, 122, 123): @@ -975,6 +998,14 @@ class SqlContractTest(unittest.TestCase): tuple(row[index] for index in (4, 5, 6, 7, 8, 9, 10, 11, 12)), ) + def test_initializers_define_expected_top_level_menu_order(self) -> None: + for filename in INITIALIZERS: + with self.subTest(filename=filename): + sql = (SQL_ROOT / filename).read_text(encoding='utf-8') + rows = _menu_rows(sql, MENU_PROMOTION_IDS) + actual = {menu_id: (row[1], int(row[2]), int(row[3])) for menu_id, row in rows.items()} + self.assertEqual(EXPECTED_TOP_LEVEL_MENU_POSITIONS, actual) + def test_business_permission_inventory_rejects_duplicate_permission(self) -> None: sql = (SQL_ROOT / INITIALIZERS[0]).read_text(encoding='utf-8') sql += """ @@ -1135,11 +1166,11 @@ class SqlContractTest(unittest.TestCase): self.assertEqual(EXPECTED_UNIQUE_KEYS[table], unique_keys) self.assertEqual(EXPECTED_FOREIGN_KEYS[table], foreign_keys) self.assertEqual(EXPECTED_CHECKS[table], checks) - _assert_business_menu_fields(self, raw) + _assert_business_menu_fields(self, raw, LEGACY_PATCH_BUSINESS_MENU_FIELDS) _assert_business_permission_inventory(self, raw) patch_rows = _business_menu_rows(raw) mysql_rows = _business_menu_rows((SQL_ROOT / 'shierfeng-fastapi.sql').read_text(encoding='utf-8')) - compared_indexes = tuple(range(15)) + compared_indexes = (0, 1, *range(4, 15)) self.assertEqual( {menu_id: tuple(row[index] for index in compared_indexes) for menu_id, row in mysql_rows.items()}, {menu_id: tuple(row[index] for index in compared_indexes) for menu_id, row in patch_rows.items()},