From a43bb1a5551534d129293e6d64844772bd1e1278 Mon Sep 17 00:00:00 2001 From: Peanut Date: Sun, 28 Jun 2026 10:20:54 +0800 Subject: [PATCH] =?UTF-8?q?docs=EF=BC=9A=E6=96=B0=E5=A2=9E=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E6=9C=8D=E5=8A=A1=E7=AE=A1=E7=90=86=E8=A7=84=E5=88=99?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AE=9E=E7=8E=B0=E8=AE=A1=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-06-27-dev-services-port-rules.md | 606 ++++++++++++++++++ 1 file changed, 606 insertions(+) create mode 100644 docs/superpowers/plans/2026-06-27-dev-services-port-rules.md diff --git a/docs/superpowers/plans/2026-06-27-dev-services-port-rules.md b/docs/superpowers/plans/2026-06-27-dev-services-port-rules.md new file mode 100644 index 0000000..8cd2499 --- /dev/null +++ b/docs/superpowers/plans/2026-06-27-dev-services-port-rules.md @@ -0,0 +1,606 @@ +--- +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` 命名和用法一致