Files
happy-life-star/docs/superpowers/plans/2026-06-27-dev-services-port-rules.md

607 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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` 命名和用法一致