diff --git a/docs/sql/create_match_live_table.sql b/docs/sql/create_match_live_table.sql index 63b14a0..089022d 100644 --- a/docs/sql/create_match_live_table.sql +++ b/docs/sql/create_match_live_table.sql @@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS `la_match_live` ( `play_url_flv` varchar(1000) NOT NULL DEFAULT '' COMMENT '标清FLV播放地址', `play_url_hd_flv` varchar(1000) NOT NULL DEFAULT '' COMMENT '高清FLV播放地址', `fetch_status` tinyint UNSIGNED NOT NULL DEFAULT 0 COMMENT '抓取状态 0-待抓取 1-成功 2-失败', - `fetch_error` varchar(255) NOT NULL DEFAULT '' COMMENT '抓取错误信息', + `fetch_error` varchar(500) NOT NULL DEFAULT '' COMMENT '抓取错误信息', `last_fetch_at` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '最后抓取时间', `next_fetch_at` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '下次抓取时间', `create_time` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建时间', diff --git a/docs/sql/insert_menu_match_live.sql b/docs/sql/insert_menu_match_live.sql new file mode 100644 index 0000000..a8cfd2c --- /dev/null +++ b/docs/sql/insert_menu_match_live.sql @@ -0,0 +1,51 @@ +INSERT INTO `la_system_menu` (`pid`, `type`, `name`, `icon`, `sort`, `perms`, `paths`, `component`, `selected`, `params`, `is_cache`, `is_show`, `is_disable`, `create_time`, `update_time`) +SELECT COALESCE((SELECT `id` FROM `la_system_menu` WHERE `perms` = 'match.match/lists' LIMIT 1), 0), 'C', '直播线路管理', '', 0, 'match.matchLive/lists', 'match_live', 'match/lists/index', 'match/lists', '', 0, 0, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP() +WHERE NOT EXISTS ( + SELECT 1 FROM `la_system_menu` + WHERE `perms` = 'match.matchLive/lists' +); + +INSERT INTO `la_system_menu` (`pid`, `type`, `name`, `icon`, `sort`, `perms`, `paths`, `component`, `selected`, `params`, `is_cache`, `is_show`, `is_disable`, `create_time`, `update_time`) +SELECT (SELECT `id` FROM `la_system_menu` WHERE `perms` = 'match.matchLive/lists' LIMIT 1), 'A', '直播线路详情', '', 0, 'match.matchLive/detail', '', '', '', '', 0, 0, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP() +WHERE NOT EXISTS ( + SELECT 1 FROM `la_system_menu` + WHERE `perms` = 'match.matchLive/detail' +); + +INSERT INTO `la_system_menu` (`pid`, `type`, `name`, `icon`, `sort`, `perms`, `paths`, `component`, `selected`, `params`, `is_cache`, `is_show`, `is_disable`, `create_time`, `update_time`) +SELECT (SELECT `id` FROM `la_system_menu` WHERE `perms` = 'match.matchLive/lists' LIMIT 1), 'A', '直播线路新增', '', 0, 'match.matchLive/add', '', '', '', '', 0, 0, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP() +WHERE NOT EXISTS ( + SELECT 1 FROM `la_system_menu` + WHERE `perms` = 'match.matchLive/add' +); + +INSERT INTO `la_system_menu` (`pid`, `type`, `name`, `icon`, `sort`, `perms`, `paths`, `component`, `selected`, `params`, `is_cache`, `is_show`, `is_disable`, `create_time`, `update_time`) +SELECT (SELECT `id` FROM `la_system_menu` WHERE `perms` = 'match.matchLive/lists' LIMIT 1), 'A', '直播线路编辑', '', 0, 'match.matchLive/edit', '', '', '', '', 0, 0, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP() +WHERE NOT EXISTS ( + SELECT 1 FROM `la_system_menu` + WHERE `perms` = 'match.matchLive/edit' +); + +INSERT INTO `la_system_menu` (`pid`, `type`, `name`, `icon`, `sort`, `perms`, `paths`, `component`, `selected`, `params`, `is_cache`, `is_show`, `is_disable`, `create_time`, `update_time`) +SELECT (SELECT `id` FROM `la_system_menu` WHERE `perms` = 'match.matchLive/lists' LIMIT 1), 'A', '直播线路删除', '', 0, 'match.matchLive/delete', '', '', '', '', 0, 0, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP() +WHERE NOT EXISTS ( + SELECT 1 FROM `la_system_menu` + WHERE `perms` = 'match.matchLive/delete' +); + +INSERT INTO `la_system_role_menu` (`role_id`, `menu_id`) +SELECT 1, m.`id` +FROM `la_system_menu` m +WHERE m.`perms` IN ( + 'match.matchLive/lists', + 'match.matchLive/detail', + 'match.matchLive/add', + 'match.matchLive/edit', + 'match.matchLive/delete' +) +AND NOT EXISTS ( + SELECT 1 + FROM `la_system_role_menu` rm + WHERE rm.`role_id` = 1 + AND rm.`menu_id` = m.`id` +); diff --git a/qa/backend/test_match_live_backend_static.php b/qa/backend/test_match_live_backend_static.php index fb69100..e830f91 100644 --- a/qa/backend/test_match_live_backend_static.php +++ b/qa/backend/test_match_live_backend_static.php @@ -105,17 +105,64 @@ if (!is_file($matchLiveLogicPath)) { $matchLiveLogic = file_get_contents($matchLiveLogicPath); foreach ([ - "'play_url_m3u8' => ''", - "'play_url_hd_m3u8' => ''", - "'play_url_flv' => ''", - "'play_url_hd_flv' => ''", - "'fetch_status' => 0", - "'fetch_error' => ''", - "'last_fetch_at' => 0", - "'next_fetch_at' => \$now", + '$sourceUrl = trim((string) $params[\'source_url\']);', + '$sourceUrlChanged = $sourceUrl !== trim((string) $live->source_url);', + "'source_url' => \$sourceUrl", + 'if ($sourceUrlChanged) {', + "\$saveData['play_url_m3u8'] = '';", + "\$saveData['play_url_hd_m3u8'] = '';", + "\$saveData['play_url_flv'] = '';", + "\$saveData['play_url_hd_flv'] = '';", + "\$saveData['fetch_status'] = 0;", + "\$saveData['fetch_error'] = '';", + "\$saveData['last_fetch_at'] = 0;", + "\$saveData['next_fetch_at'] = \$now;", + '$live->save($saveData);', ] as $needle) { if (strpos($matchLiveLogic, $needle) === false) { - fwrite(STDERR, "missing match live logic reset token: {$needle}\n"); + fwrite(STDERR, "missing match live logic source-url reset guard token: {$needle}\n"); + exit(1); + } +} + +$savePos = strpos($matchLiveLogic, '$live->save($saveData);'); +$guardPos = strpos($matchLiveLogic, 'if ($sourceUrlChanged) {'); +if ($savePos === false || $guardPos === false || $guardPos > $savePos) { + fwrite(STDERR, "match live logic must guard crawler resets inside source_url change branch\n"); + exit(1); +} + +$guardBody = substr($matchLiveLogic, $guardPos, $savePos - $guardPos); + +foreach ([ + "\$saveData['play_url_m3u8'] = '';", + "\$saveData['play_url_hd_m3u8'] = '';", + "\$saveData['play_url_flv'] = '';", + "\$saveData['play_url_hd_flv'] = '';", + "\$saveData['fetch_status'] = 0;", + "\$saveData['fetch_error'] = '';", + "\$saveData['last_fetch_at'] = 0;", + "\$saveData['next_fetch_at'] = \$now;", +] as $needle) { + if (strpos($guardBody, $needle) === false) { + fwrite(STDERR, "match live logic reset token not scoped to source_url change guard: {$needle}\n"); + exit(1); + } +} + +$unconditionalSaveBody = preg_replace('/if \\(\\$sourceUrlChanged\\) \\{.*?\\}/s', '', $matchLiveLogic, 1); +foreach ([ + "\$saveData['play_url_m3u8'] = '';", + "\$saveData['play_url_hd_m3u8'] = '';", + "\$saveData['play_url_flv'] = '';", + "\$saveData['play_url_hd_flv'] = '';", + "\$saveData['fetch_status'] = 0;", + "\$saveData['fetch_error'] = '';", + "\$saveData['last_fetch_at'] = 0;", + "\$saveData['next_fetch_at'] = \$now;", +] as $needle) { + if (strpos($unconditionalSaveBody, $needle) !== false) { + fwrite(STDERR, "match live logic still unconditionally resets crawler state: {$needle}\n"); exit(1); } } diff --git a/qa/backend/test_match_live_schema_static.php b/qa/backend/test_match_live_schema_static.php index 3d944cb..8402b59 100644 --- a/qa/backend/test_match_live_schema_static.php +++ b/qa/backend/test_match_live_schema_static.php @@ -27,7 +27,7 @@ $requiredSql = [ '`play_url_flv` varchar(1000)', '`play_url_hd_flv` varchar(1000)', '`fetch_status` tinyint', - '`fetch_error` varchar(255)', + '`fetch_error` varchar(500)', '`last_fetch_at` int UNSIGNED NOT NULL', '`next_fetch_at` int UNSIGNED NOT NULL', '`create_time` int UNSIGNED NOT NULL', diff --git a/qa/frontend/test_match_live_admin_static.py b/qa/frontend/test_match_live_admin_static.py index 3295890..fb06797 100644 --- a/qa/frontend/test_match_live_admin_static.py +++ b/qa/frontend/test_match_live_admin_static.py @@ -2,9 +2,11 @@ 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: @@ -15,6 +17,8 @@ def assert_contains(path: Path, needle: str) -> None: 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") assert_contains(MATCH_INDEX, 'type="expand"') assert_contains(MATCH_INDEX, "MatchLiveTable") @@ -88,6 +92,15 @@ def main() -> None: ): 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.matchLive/delete", + ): + assert needle in menu_sql, f"{MENU_SQL} missing permission token: {needle}" + print("match live admin static checks passed") diff --git a/server/app/adminapi/logic/match/MatchLiveLogic.php b/server/app/adminapi/logic/match/MatchLiveLogic.php index 123ee51..d844e51 100644 --- a/server/app/adminapi/logic/match/MatchLiveLogic.php +++ b/server/app/adminapi/logic/match/MatchLiveLogic.php @@ -49,21 +49,28 @@ class MatchLiveLogic extends BaseLogic $oldMatchId = (int) $live->match_id; $newMatchId = (int) $params['match_id']; $now = time(); + $sourceUrl = trim((string) $params['source_url']); + $sourceUrlChanged = $sourceUrl !== trim((string) $live->source_url); - $live->save([ + $saveData = [ 'match_id' => $newMatchId, 'title' => trim((string) $params['title']), - 'source_url' => trim((string) $params['source_url']), - 'play_url_m3u8' => '', - 'play_url_hd_m3u8' => '', - 'play_url_flv' => '', - 'play_url_hd_flv' => '', - 'fetch_status' => 0, - 'fetch_error' => '', - 'last_fetch_at' => 0, - 'next_fetch_at' => $now, + 'source_url' => $sourceUrl, 'update_time' => $now, - ]); + ]; + + if ($sourceUrlChanged) { + $saveData['play_url_m3u8'] = ''; + $saveData['play_url_hd_m3u8'] = ''; + $saveData['play_url_flv'] = ''; + $saveData['play_url_hd_flv'] = ''; + $saveData['fetch_status'] = 0; + $saveData['fetch_error'] = ''; + $saveData['last_fetch_at'] = 0; + $saveData['next_fetch_at'] = $now; + } + + $live->save($saveData); if ($oldMatchId > 0 && $oldMatchId !== $newMatchId) { MatchLiveService::syncLegacyLiveUrl($oldMatchId);