from pathlib import Path ROOT = Path(r"D:\www\sport-era\admin") REPO_ROOT = Path(__file__).resolve().parents[2] MATCH_API = ROOT / "src" / "api" / "match.ts" MATCH_INDEX = ROOT / "src" / "views" / "match" / "lists" / "index.vue" MATCH_LIVE_TABLE = ROOT / "src" / "views" / "match" / "lists" / "components" / "MatchLiveTable.vue" MENU_SQL = REPO_ROOT / "docs" / "sql" / "insert_menu_match_live.sql" def assert_contains(path: Path, needle: str) -> None: content = path.read_text(encoding="utf-8") assert needle in content, f"{path} missing: {needle}" def main() -> None: match_api = MATCH_API.read_text(encoding="utf-8") match_live_table = MATCH_LIVE_TABLE.read_text(encoding="utf-8") assert MENU_SQL.is_file(), f"missing menu seed file: {MENU_SQL}" menu_sql = MENU_SQL.read_text(encoding="utf-8") normalized_menu_sql = " ".join(menu_sql.split()) assert_contains(MATCH_INDEX, 'type="expand"') assert_contains(MATCH_INDEX, "MatchLiveTable") for needle in ( "matchLiveLists", "matchLiveDetail", "matchLiveAdd", "matchLiveEdit", "matchLiveDelete", ): assert needle in match_api, f"{MATCH_API} missing: {needle}" for needle in ( "play_url_m3u8", "fetch_status", "source_url", ): assert needle in match_live_table, f"{MATCH_LIVE_TABLE} missing: {needle}" readonly_fields = ( 'play_url_m3u8" readonly', 'play_url_hd_m3u8" readonly', 'play_url_flv" readonly', 'play_url_hd_flv" readonly', 'formatFetchStatus(editData.fetch_status)" readonly', 'editData.last_fetch_at" readonly', ) for needle in readonly_fields: assert needle in match_live_table, f"{MATCH_LIVE_TABLE} missing readonly guard: {needle}" handle_edit_block = match_live_table.split("const handleEdit = async (id: number) => {", 1)[1].split( "const handleSubmit = async () => {", 1 )[0] assert "resetEditData()" in handle_edit_block, f"{MATCH_LIVE_TABLE} missing resetEditData usage in handleEdit" assert "const res = await matchLiveDetail" in handle_edit_block, f"{MATCH_LIVE_TABLE} missing detail request" assert ( handle_edit_block.index("resetEditData()") < handle_edit_block.index("const res = await matchLiveDetail") ), "handleEdit must reset editData before matchLiveDetail response is applied" assert "const params = {" in match_live_table, f"{MATCH_LIVE_TABLE} missing submit payload" for needle in ( "id: editData.id", "match_id: props.matchId", "title: editData.title", "source_url: editData.source_url", ): assert needle in match_live_table, f"{MATCH_LIVE_TABLE} missing submit field: {needle}" forbidden_submit_fields = ( "play_url_m3u8:", "play_url_hd_m3u8:", "play_url_flv:", "play_url_hd_flv:", "fetch_status:", "last_fetch_at:", "create_time:", "update_time:", ) params_slice = match_live_table.split("const params = {", 1)[1].split("\n }", 1)[0] for needle in forbidden_submit_fields: assert needle not in params_slice, f"submit payload must not include readonly field: {needle}" for needle in ( "status === 0", "status === 1", "status === 2", "status === '0'", "status === '1'", "status === '2'", ): assert needle in match_live_table, f"{MATCH_LIVE_TABLE} missing fetch_status mapping: {needle}" for needle in ( "match.matchLive/lists", "match.matchLive/detail", "match.matchLive/add", "match.matchLive/edit", "match.match/lists", "match.matchLive/delete", ): assert needle in menu_sql, f"{MENU_SQL} missing permission token: {needle}" forbidden_menu_fallbacks = ( "COALESCE(", ", 0), 'C', '直播线路管理'", "SELECT 0, 'C', '直播线路管理'", "VALUES (0, 'C', '直播线路管理'", ) for needle in forbidden_menu_fallbacks: assert needle not in menu_sql, f"{MENU_SQL} must not contain top-level fallback: {needle}" assert ( "FROM `la_system_menu` parent" in menu_sql and "parent.`perms` = 'match.match/lists'" in menu_sql ), f"{MENU_SQL} must bind the match live menu to parent match.match/lists explicitly" assert ( "SELECT parent.`id`, 'C', '直播线路管理'" in menu_sql and "WHERE NOT EXISTS (" in menu_sql ), f"{MENU_SQL} must insert the list menu only when the parent match.match/lists record exists" assert ( "SELECT (SELECT `id` FROM `la_system_menu` WHERE `perms` = 'match.matchLive/lists' LIMIT 1)" in normalized_menu_sql ), f"{MENU_SQL} child permissions must bind to the inserted match.matchLive/lists menu" print("match live admin static checks passed") if __name__ == "__main__": main()