初始化本地仓库

This commit is contained in:
2026-08-31 11:33:59 +08:00
commit 663f74b633
2390 changed files with 402918 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
@@ -0,0 +1,173 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import re
from pathlib import Path
from typing import List, Tuple
INDEX_BLOCK_RE = re.compile(
r"#\[OA\\Get\((?P<oa_get>.*?)\)\]\s*"
r"#\[Permission\((?P<permission>.*?)\)\]\s*"
r"#\[.*?\]\s*"
r"public function index\(Request \$request\): \\support\\Response\s*\{(?P<index_body>.*?)\}",
re.S,
)
UPDATE_METHOD_RE = re.compile(
r"(?P<indent>\s*)public function update\(Request \$request\): \\support\\Response\s*\{(?P<body>.*?)\n(?P=indent)\}",
re.S,
)
EXPORT_METHOD_RE = re.compile(r"public function export\(Request \$request\): \\support\\Response")
EXTENDS_CRUD_RE = re.compile(r"class\s+\w+\s+extends\s+Crud")
LIST_PERMISSION_RE = re.compile(r"['\"]([^'\"]+:list)['\"]")
PATH_RE = re.compile(r"path:\s*['\"]([^'\"]+)['\"]")
TAGS_RE = re.compile(r"tags:\s*(\[[^\]]*\])")
SKIP_PATH_KEYWORDS = (
'/monitor/online-user',
'/monitor/server',
'/monitor/redis',
'/login',
'/profile',
)
def detect_newline(text: str) -> str:
return '\r\n' if '\r\n' in text else '\n'
def normalize_permission(raw: str) -> str | None:
match = LIST_PERMISSION_RE.search(raw)
return match.group(1) if match else None
def build_export_block(path: str, tags_expr: str, permission_code: str, newline: str, indent: str) -> str:
export_path = f"{path.rstrip('/')}/export"
lines = [
'',
f"{indent}#[OA\\Post(",
f"{indent} path: '{export_path}',",
f"{indent} summary: '导出',",
f"{indent} tags: {tags_expr}",
f"{indent})]",
f"{indent}#[Permission(code: '{permission_code}', description: '导出')]",
f"{indent}#[SimpleResponse(schema: [], example: [])]",
f"{indent}public function export(Request $request): \\support\\Response",
f"{indent}{{",
f"{indent} return parent::export($request);",
f"{indent}}}",
]
return newline.join(lines)
def should_skip_path(path: str) -> bool:
return any(keyword in path for keyword in SKIP_PATH_KEYWORDS)
def patch_file(file_path: Path, apply: bool) -> Tuple[str, str]:
original = file_path.read_text(encoding='utf-8')
if not EXTENDS_CRUD_RE.search(original):
return 'skip', 'not-crud'
if EXPORT_METHOD_RE.search(original):
return 'skip', 'has-export'
if '$this->service' not in original:
return 'skip', 'no-service-assign'
index_match = INDEX_BLOCK_RE.search(original)
if not index_match:
return 'skip', 'no-standard-index'
index_body = index_match.group('index_body')
if 'parent::index($request)' not in index_body:
return 'skip', 'custom-index'
permission_code = normalize_permission(index_match.group('permission'))
if not permission_code:
return 'skip', 'no-list-permission'
path_match = PATH_RE.search(index_match.group('oa_get'))
if not path_match:
return 'skip', 'no-path'
path = path_match.group(1)
if should_skip_path(path):
return 'skip', 'skip-special-path'
tags_match = TAGS_RE.search(index_match.group('oa_get'))
if not tags_match:
return 'skip', 'no-tags'
tags_expr = tags_match.group(1)
update_match = UPDATE_METHOD_RE.search(original)
if not update_match:
return 'skip', 'no-update-method'
update_body = update_match.group('body')
if 'parent::update($request)' not in update_body:
return 'skip', 'custom-update'
indent = update_match.group('indent')
newline = detect_newline(original)
export_block = build_export_block(path, tags_expr, permission_code, newline, indent)
insert_pos = update_match.end()
patched = original[:insert_pos] + export_block + original[insert_pos:]
if patched == original:
return 'skip', 'no-change'
if apply:
file_path.write_text(patched, encoding='utf-8', newline='')
return 'patched', path
return 'preview', path
def collect_targets(backend_root: Path) -> List[Path]:
return sorted(
path for path in backend_root.rglob('*.php')
if 'vendor' not in path.parts
)
def main() -> int:
parser = argparse.ArgumentParser(description='批量为标准 CRUD 控制器补 export() 路由')
parser.add_argument('--apply', action='store_true', help='真正写入文件;默认仅预览')
parser.add_argument('--backend-root', default=None, help='backend 根目录路径,默认自动推断')
args = parser.parse_args()
script_path = Path(__file__).resolve()
backend_root = Path(args.backend_root).resolve() if args.backend_root else script_path.parents[2]
if not backend_root.exists():
print(f'[ERROR] backend 根目录不存在: {backend_root}')
return 1
results: List[Tuple[str, str, str]] = []
for file_path in collect_targets(backend_root):
status, detail = patch_file(file_path, args.apply)
if status in {'patched', 'preview'}:
results.append((status, detail, str(file_path)))
mode = 'APPLY' if args.apply else 'PREVIEW'
print(f'=== {mode} RESULT ===')
if not results:
print('没有发现可补 export() 的标准 CRUD 控制器。')
return 0
for status, detail, file_name in results:
print(f'[{status}] {detail} -> {file_name}')
print(f'总计: {len(results)} 个文件')
if not args.apply:
print('提示: 追加 --apply 才会真正写入文件')
return 0
if __name__ == '__main__':
raise SystemExit(main())