18 KiB
author, created_at, purpose
| author | created_at | purpose |
|---|---|---|
| claude | 2026-06-27 | 本地服务管理规则优化实现计划 |
本地服务管理规则优化实现计划
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- 端口改为 5178web/.env.development- 添加VITE_PORT=5178web-admin/vite.config.ts- 端口改为 5179web-admin/.env.development-VITE_APP_PORT改为 5179mini-program/vite.config.js- 端口改为 5180mini-program/.env.development- 添加VITE_PORT=5180life-script/vite.config.js- 端口改为 5181life-script/.env.development- 添加VITE_PORT=5181CLAUDE.md- 更新本地服务管理规则
Task 1: 修改 dev-services.py 固定前端端口并保护端口分配
Files:
-
Modify:
dev-services.py -
Step 1: 在常量区添加固定前端端口映射
找到 DEFAULT_PORTS 定义(约第 58-68 行),在其后添加:
# 前端/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}:
修改前:
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
修改后:
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()强制前端固定端口
修改前:
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
修改后:
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 语法
cd "G:/IdeaProjects/emotion-museun"
python dev-services.py discover
预期输出:成功列出所有发现的服务,且前端服务端口为 5178-5181
- Step 5: 提交
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 一起)添加:
# 必须重启才能生效的文件模式
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() 函数附近添加:
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 解析可能类似:
restart_parser = subparsers.add_parser("restart", help="重启所有或指定服务")
restart_parser.add_argument("service", nargs="?", help="指定服务名")
修改后:
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() 函数签名当前为:
def cmd_restart(args):
services = discover_services()
services = _filter_services(services, args.service)
修改后:
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: 验证热加载保护
- 修改一个
.vue文件(例如mini-program/src/pages/main/ScriptView.vue) - 运行:
预期:提示无需重启,并建议使用
python dev-services.py restart mini-program--force - 运行:
预期:强制重启
python dev-services.py restart mini-program --force
- Step 6: 提交
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:
server: {
port: 5178,
// ... 其他配置
}
修改/创建 web/.env.development,添加:
VITE_PORT=5178
- Step 2: 修改 web-admin/ 端口为 5179
修改 web-admin/vite.config.ts 中的 server.port:
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:
server: {
port: 5180,
// ... 其他配置
}
修改/创建 mini-program/.env.development,添加:
VITE_PORT=5180
- Step 4: 修改 life-script/ 端口为 5181
修改 life-script/vite.config.js 中的 server.port:
server: {
port: 5181,
// ... 其他配置
}
修改/创建 life-script/.env.development,添加:
VITE_PORT=5181
- Step 5: 验证端口配置
cd "G:/IdeaProjects/emotion-museun"
python dev-services.py discover
预期输出:
-
web 服务端口为 5178
-
web-admin 服务端口为 5179
-
mini-program 服务端口为 5180
-
life-script 服务端口为 5181
-
Step 6: 提交
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 中找到现有的"本地服务管理规则"或"热加载规则"章节,合并更新为统一的"本地服务管理规则(强制)":
## 本地服务管理规则(强制)
**启动、重启、停止本地前后端服务时,必须使用项目根目录下的 `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: 提交
git add CLAUDE.md
git commit -m "docs: 更新本地服务管理规则
- 强制使用 dev-services.py
- 明确前端/H5 固定端口
- 新增禁止无意义重启规则"
Task 5: 最终验证
Files:
-
无需修改文件,仅做验证
-
Step 1: 验证 dev-services.py 发现服务端口正确
cd "G:/IdeaProjects/emotion-museun"
python dev-services.py discover
预期输出:
-
web 端口 5178
-
web-admin 端口 5179
-
mini-program 端口 5180
-
life-script 端口 5181
-
后端服务保持原有端口
-
Step 2: 验证热加载保护
- 修改
mini-program/src/pages/main/ScriptView.vue中任意一处(例如加一个空行) - 运行:
预期:提示无需重启
python dev-services.py restart mini-program - 运行:
预期:强制重启
python dev-services.py restart mini-program --force
- Step 3: 验证必须重启的场景
- 修改
mini-program/vite.config.js中任意一处 - 运行:
预期:正常允许重启
python dev-services.py restart mini-program
- Step 4: 验证端口冲突检测
- 手动启动一个占用 5178 端口的进程(例如另一个 node 服务)
- 运行:
预期:报错,提示端口 5178 被占用
python dev-services.py start web - 终止占用进程
- Step 5: 检查 CLAUDE.md 一致性
快速浏览 CLAUDE.md,确认:
-
没有重复的本地服务管理规则
-
前端端口说明与新的固定端口表一致
-
热加载规则与 dev-services.py 行为一致
-
Step 6: 提交验证修复(如有)
如果在验证中修复了问题:
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命名和用法一致