--- author: claude created_at: 2026-06-27 purpose: 本地服务管理规则优化实现计划 --- # 本地服务管理规则优化实现计划 > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 优化 dev-services.py,强制使用脚本管理本地服务,固定前端/H5 端口从 5178 开始累加,并在无必须重启的变更时禁止 restart **Architecture:** 在 dev-services.py 中内置固定前端端口映射并强制使用,修改各前端项目 vite 配置和 env 文件同步端口,新增 restart 命令热加载保护,更新 CLAUDE.md 规则 **Tech Stack:** Python, Vite, UniApp, Spring Boot --- ## 文件结构 **修改:** - `dev-services.py` - 添加固定端口映射、强制端口分配、热加载保护 - `web/vite.config.ts` - 端口改为 5178 - `web/.env.development` - 添加 `VITE_PORT=5178` - `web-admin/vite.config.ts` - 端口改为 5179 - `web-admin/.env.development` - `VITE_APP_PORT` 改为 5179 - `mini-program/vite.config.js` - 端口改为 5180 - `mini-program/.env.development` - 添加 `VITE_PORT=5180` - `life-script/vite.config.js` - 端口改为 5181 - `life-script/.env.development` - 添加 `VITE_PORT=5181` - `CLAUDE.md` - 更新本地服务管理规则 --- ### Task 1: 修改 dev-services.py 固定前端端口并保护端口分配 **Files:** - Modify: `dev-services.py` - [ ] **Step 1: 在常量区添加固定前端端口映射** 找到 `DEFAULT_PORTS` 定义(约第 58-68 行),在其后添加: ```python # 前端/H5 服务固定端口映射(按项目目录名匹配) FIXED_FRONTEND_PORTS = { "web": 5178, "web-admin": 5179, "mini-program": 5180, "life-script": 5181, } ``` - [ ] **Step 2: 修改 `_update_service_port()` 函数以支持 Vite 端口** 当前 `_update_service_port()` 已处理 `--port` 和 `--server.port` 参数,还需要处理 Vite 的 `server.port` 配置更新。函数本身不需要修改,因为端口值更新后,`assign_unique_ports()` 会通过 `_service_has_explicit_port()` 判断。 但需要注意:对于 Vite/UniApp/Taro 服务,如果 `start_cmd` 中没有 `--port` 参数(当前 UniApp 的 `dev:h5` 脚本就没有),需要在启动命令前注入 `VITE_PORT` 环境变量或在命令中追加 `--port` 参数。 修改 `_update_service_port()` 函数,使其在更新 `start_cmd` 时,如果命令中没有 `--port` 参数,则在命令末尾追加 `--port {port}`: 修改前: ```python def _update_service_port(svc: Service, port: int): old_port = svc.port if old_port == port: return svc.port = port svc.health_url = re.sub(r":\d+", f":{port}", svc.health_url, count=1) updated = [] skip_next = False for index, part in enumerate(svc.start_cmd): if skip_next: skip_next = False continue if part == "--port" and index + 1 < len(svc.start_cmd): updated.extend([part, str(port)]) skip_next = True elif part.startswith("--port="): updated.append(f"--port={port}") elif f"--server.port={old_port}" in part: updated.append(part.replace(f"--server.port={old_port}", f"--server.port={port}")) else: updated.append(part) svc.start_cmd = updated ``` 修改后: ```python def _update_service_port(svc: Service, port: int): old_port = svc.port if old_port == port: return svc.port = port svc.health_url = re.sub(r":\d+", f":{port}", svc.health_url, count=1) updated = [] skip_next = False has_port_arg = False for index, part in enumerate(svc.start_cmd): if skip_next: skip_next = False continue if part == "--port": has_port_arg = True if index + 1 < len(svc.start_cmd): updated.extend([part, str(port)]) skip_next = True elif part.startswith("--port="): has_port_arg = True updated.append(f"--port={port}") elif f"--server.port={old_port}" in part: updated.append(part.replace(f"--server.port={old_port}", f"--server.port={port}")) else: updated.append(part) # Vite/UniApp/Taro/Next 服务如果没有 --port 参数,追加 --port if not has_port_arg and svc.project_type in {"vite", "uniapp", "taro", "next"}: updated.extend(["--port", str(port)]) svc.start_cmd = updated ``` - [ ] **Step 3: 重写 `assign_unique_ports()` 强制前端固定端口** 修改前: ```python def assign_unique_ports(services: list[Service]) -> list[Service]: reserved = {svc.port for svc in services if _service_has_explicit_port(svc)} used = set() for svc in services: port = svc.port if port in used or (port in reserved and not _service_has_explicit_port(svc)): while port in used or port in reserved: port += 1 log(f"{svc.name} 端口 {svc.port} 与其他服务冲突,自动调整为 {port}", "WARN") _update_service_port(svc, port) used.add(svc.port) return services ``` 修改后: ```python def assign_unique_ports(services: list[Service]) -> list[Service]: # 按目录名匹配固定前端端口 dir_name_to_service = {} for svc in services: dir_name = svc.project_dir.name if dir_name in FIXED_FRONTEND_PORTS: dir_name_to_service[dir_name] = svc # 先为固定前端服务分配端口 for dir_name, svc in dir_name_to_service.items(): fixed_port = FIXED_FRONTEND_PORTS[dir_name] if svc.port != fixed_port: log(f"{svc.name} 使用固定前端端口 {fixed_port}", "INFO") _update_service_port(svc, fixed_port) # 检查固定端口冲突 used_ports = {} for svc in services: port = svc.port if port in used_ports: other = used_ports[port] log(f"端口冲突: {svc.name} ({port}) 与 {other.name} ({port}) 冲突", "ERROR") sys.exit(1) used_ports[port] = svc # 非固定前端服务按原有逻辑处理冲突 reserved = {svc.port for svc in services} for svc in services: dir_name = svc.project_dir.name if dir_name in FIXED_FRONTEND_PORTS: continue # 固定前端服务已处理 port = svc.port if port in used_ports and used_ports[port] is not svc: while port in used_ports: port += 1 log(f"{svc.name} 端口 {svc.port} 与其他服务冲突,自动调整为 {port}", "WARN") _update_service_port(svc, port) return services ``` - [ ] **Step 4: 验证 dev-services.py 语法** ```bash cd "G:/IdeaProjects/emotion-museun" python dev-services.py discover ``` 预期输出:成功列出所有发现的服务,且前端服务端口为 5178-5181 - [ ] **Step 5: 提交** ```bash git add dev-services.py git commit -m "feat: dev-services.py 强制前端服务使用固定端口 - 新增 FIXED_FRONTEND_PORTS 映射 - 前端/H5 端口从 5178 开始固定分配 - 端口冲突时不再自动递增前端服务端口,改为报错" ``` --- ### Task 2: 为 dev-services.py 添加 restart 热加载保护 **Files:** - Modify: `dev-services.py` - [ ] **Step 1: 添加"必须重启"文件模式常量** 在 `RESTART_REQUIRED_PATTERNS` 常量区(与 `FIXED_FRONTEND_PORTS` 一起)添加: ```python # 必须重启才能生效的文件模式 RESTART_REQUIRED_PATTERNS = [ re.compile(r'vite\.config\.(ts|js|mjs|cjs)$'), re.compile(r'tsconfig\.json$'), re.compile(r'\.env(\.[^/]*)?$'), re.compile(r'package\.json$'), re.compile(r'application.*\.ya?ml$'), re.compile(r'pom\.xml$'), re.compile(r'build\.gradle(\.kts)?$'), ] ``` 注意:需要确保文件顶部已导入 `re` 和 `sys`。 - [ ] **Step 2: 添加 `_should_restart_for_changes()` 函数** 在 `assign_unique_ports()` 函数附近添加: ```python def _should_restart_for_changes(service_dir: Path) -> tuple[bool, list[str]]: """ 检查指定服务目录下是否有必须重启才能生效的变更。 返回: (是否需要重启, 已修改文件列表) """ try: result = subprocess.run( ["git", "diff", "--name-only", "--", str(service_dir)], capture_output=True, text=True, cwd=SCRIPT_DIR, check=True, ) changed_files = [line.strip() for line in result.stdout.splitlines() if line.strip()] except subprocess.CalledProcessError: # 非 git 仓库或 git 命令失败,默认允许重启 return True, [] except FileNotFoundError: return True, [] if not changed_files: return False, [] for file_path in changed_files: for pattern in RESTART_REQUIRED_PATTERNS: if pattern.search(file_path): return True, changed_files return False, changed_files ``` - [ ] **Step 3: 修改 restart 命令解析支持 --force** 找到 `main()` 函数中的 `restart` 子命令解析部分(约文件末尾),添加 `--force` 参数。 当前 restart 解析可能类似: ```python restart_parser = subparsers.add_parser("restart", help="重启所有或指定服务") restart_parser.add_argument("service", nargs="?", help="指定服务名") ``` 修改后: ```python restart_parser = subparsers.add_parser("restart", help="重启所有或指定服务") restart_parser.add_argument("service", nargs="?", help="指定服务名") restart_parser.add_argument( "--force", action="store_true", help="强制重启,忽略热加载保护" ) ``` - [ ] **Step 4: 在 restart 执行前加入热加载保护** 找到 `cmd_restart()` 函数,在其开头添加检测逻辑。 假设 `cmd_restart()` 函数签名当前为: ```python def cmd_restart(args): services = discover_services() services = _filter_services(services, args.service) ``` 修改后: ```python def cmd_restart(args): services = discover_services() services = _filter_services(services, args.service) if not args.force: blocked = [] for svc in services: should_restart, changed_files = _should_restart_for_changes(svc.project_dir) if not should_restart: if changed_files: blocked.append((svc.name, changed_files)) else: blocked.append((svc.name, [])) if blocked: log("检测到以下服务无需重启(当前修改支持热加载):", "WARN") for name, files in blocked: if files: log(f" - {name}: 修改文件 {', '.join(files)}", "WARN") else: log(f" - {name}: 未检测到代码变更", "WARN") log("如需强制重启,请使用: python dev-services.py restart --force", "WARN") sys.exit(0) # 原有 restart 逻辑继续... ``` 注意:实际代码中 `cmd_restart()` 的函数名和参数可能不同,请根据实际代码调整。 - [ ] **Step 5: 验证热加载保护** 1. 修改一个 `.vue` 文件(例如 `mini-program/src/pages/main/ScriptView.vue`) 2. 运行: ```bash python dev-services.py restart mini-program ``` 预期:提示无需重启,并建议使用 `--force` 3. 运行: ```bash python dev-services.py restart mini-program --force ``` 预期:强制重启 - [ ] **Step 6: 提交** ```bash git add dev-services.py git commit -m "feat: dev-services.py restart 命令新增热加载保护 - 无必须重启的变更时禁止 restart - 支持 --force 参数强制重启 - 避免无意义重启前端热加载服务" ``` --- ### Task 3: 同步各前端项目端口配置 **Files:** - Modify: `web/vite.config.ts`, `web/.env.development` - Modify: `web-admin/vite.config.ts`, `web-admin/.env.development` - Modify: `mini-program/vite.config.js`, `mini-program/.env.development` - Modify: `life-script/vite.config.js`, `life-script/.env.development` - [ ] **Step 1: 修改 web/ 端口为 5178** 修改 `web/vite.config.ts` 中的 `server.port`: ```typescript server: { port: 5178, // ... 其他配置 } ``` 修改/创建 `web/.env.development`,添加: ``` VITE_PORT=5178 ``` - [ ] **Step 2: 修改 web-admin/ 端口为 5179** 修改 `web-admin/vite.config.ts` 中的 `server.port`: ```typescript server: { port: 5179, // ... 其他配置 } ``` 修改 `web-admin/.env.development`,将 `VITE_APP_PORT` 改为: ``` VITE_APP_PORT=5179 ``` - [ ] **Step 3: 修改 mini-program/ 端口为 5180** 修改 `mini-program/vite.config.js` 中的 `server.port`: ```javascript server: { port: 5180, // ... 其他配置 } ``` 修改/创建 `mini-program/.env.development`,添加: ``` VITE_PORT=5180 ``` - [ ] **Step 4: 修改 life-script/ 端口为 5181** 修改 `life-script/vite.config.js` 中的 `server.port`: ```javascript server: { port: 5181, // ... 其他配置 } ``` 修改/创建 `life-script/.env.development`,添加: ``` VITE_PORT=5181 ``` - [ ] **Step 5: 验证端口配置** ```bash cd "G:/IdeaProjects/emotion-museun" python dev-services.py discover ``` 预期输出: - web 服务端口为 5178 - web-admin 服务端口为 5179 - mini-program 服务端口为 5180 - life-script 服务端口为 5181 - [ ] **Step 6: 提交** ```bash git add web/ web-admin/ mini-program/ life-script/ git commit -m "chore: 统一前端/H5 服务固定端口 - web: 5178 - web-admin: 5179 - mini-program: 5180 - life-script: 5181" ``` --- ### Task 4: 更新 CLAUDE.md 规则文档 **Files:** - Modify: `CLAUDE.md` - [ ] **Step 1: 添加/更新本地服务管理规则章节** 在 `CLAUDE.md` 中找到现有的"本地服务管理规则"或"热加载规则"章节,合并更新为统一的"本地服务管理规则(强制)": ```markdown ## 本地服务管理规则(强制) **启动、重启、停止本地前后端服务时,必须使用项目根目录下的 `dev-services.py` 脚本。** ### 强制要求 - ✅ 必须使用:`python dev-services.py [start|stop|restart|status|discover] [服务名]` - ❌ 禁止直接使用 `npm run dev` / `pnpm run dev` 启动前端 - ❌ 禁止直接使用 `npm run dev:h5` 启动小程序 H5 - ❌ 禁止直接使用 `mvn spring-boot:run` 启动后端 - ❌ 禁止无意义重启支持热加载的服务 ### 前端/H5 固定端口 | 服务 | 目录 | 固定端口 | |---|---|---| | web | `web/` | 5178 | | web-admin | `web-admin/` | 5179 | | mini-program | `mini-program/` | 5180 | | life-script | `life-script/` | 5181 | ### 热加载规则 1. **不需要重启的场景**(热加载自动生效) - 前端:修改 `.vue`、`.tsx`、`.ts`、`.js`、`.css`、`.scss` 等源码文件 - 样式、模板、静态资源等修改 2. **需要重启的场景** - 前端:修改 `vite.config.ts`、`tsconfig.json`、`.env`、`package.json`、别名配置等 - 后端:按现有后端规则处理(本地不启动后端) 3. **禁止无意义重启** - 执行 `python dev-services.py restart` 时,脚本会自动检测是否有必须重启才能生效的变更 - 如果只有支持热加载的源码文件变更,脚本会拒绝重启并提示: > 当前修改只涉及支持热加载的源码文件,无需重启。如需强制重启,请使用 `python dev-services.py restart --force` - 确需强制重启时,使用 `--force` 参数 ``` - [ ] **Step 2: 删除冲突的旧规则** 检查 CLAUDE.md 中是否有与上述新规则冲突的旧规则(如旧的前端端口说明、热加载规则),删除或更新它们,确保文档内部一致。 - [ ] **Step 3: 提交** ```bash git add CLAUDE.md git commit -m "docs: 更新本地服务管理规则 - 强制使用 dev-services.py - 明确前端/H5 固定端口 - 新增禁止无意义重启规则" ``` --- ### Task 5: 最终验证 **Files:** - 无需修改文件,仅做验证 - [ ] **Step 1: 验证 dev-services.py 发现服务端口正确** ```bash cd "G:/IdeaProjects/emotion-museun" python dev-services.py discover ``` 预期输出: - web 端口 5178 - web-admin 端口 5179 - mini-program 端口 5180 - life-script 端口 5181 - 后端服务保持原有端口 - [ ] **Step 2: 验证热加载保护** 1. 修改 `mini-program/src/pages/main/ScriptView.vue` 中任意一处(例如加一个空行) 2. 运行: ```bash python dev-services.py restart mini-program ``` 预期:提示无需重启 3. 运行: ```bash python dev-services.py restart mini-program --force ``` 预期:强制重启 - [ ] **Step 3: 验证必须重启的场景** 1. 修改 `mini-program/vite.config.js` 中任意一处 2. 运行: ```bash python dev-services.py restart mini-program ``` 预期:正常允许重启 - [ ] **Step 4: 验证端口冲突检测** 1. 手动启动一个占用 5178 端口的进程(例如另一个 node 服务) 2. 运行: ```bash python dev-services.py start web ``` 预期:报错,提示端口 5178 被占用 3. 终止占用进程 - [ ] **Step 5: 检查 CLAUDE.md 一致性** 快速浏览 `CLAUDE.md`,确认: - 没有重复的本地服务管理规则 - 前端端口说明与新的固定端口表一致 - 热加载规则与 dev-services.py 行为一致 - [ ] **Step 6: 提交验证修复(如有)** 如果在验证中修复了问题: ```bash git add dev-services.py CLAUDE.md git commit -m "fix: 修复本地服务管理规则验证中的问题 [描述具体修复]" ``` 如果全部通过,无需额外提交。 --- ## 自审检查 - **Spec 覆盖:** - ✅ dev-services.py 固定前端端口 → Task 1 - ✅ 热加载保护 → Task 2 - ✅ 前端项目端口配置同步 → Task 3 - ✅ CLAUDE.md 规则更新 → Task 4 - ✅ 验证 → Task 5 - **无占位符:** 计划中没有 TBD/TODO - **类型一致:** `FIXED_FRONTEND_PORTS`、`RESTART_REQUIRED_PATTERNS`、`_should_restart_for_changes` 命名和用法一致