提交 e3ace44f authored 作者: 陈泽健's avatar 陈泽健

feat(service-manage): 实现 5.44 代理转发 - 授权/目标/文档管理

- 新增 five44_client.py 代理客户端,完整移植 5.44 认证签名算法
- 重构 service_manage.py 业务层,接入 5.44 代理转发(含自动重登)
- 更新路由层,授权/项目/文档操作全部支持代理模式
- 未配置目标时自动降级本地占位,保持向后兼容
- 授权页面 JS 完整实现(下载/上传/保存/回填)
- 基础模板新增 Toast 通知系统
- 整理 8 份 PRD 文档到 Docs/服务管理/ 分类目录
- 更新 HANDOFF 交接文档记录当前进度
- 238 单元测试全部通过
Co-Authored-By: 's avatarClaude <noreply@anthropic.com>
上级 6a630735
此差异已折叠。
# PRD_计划执行_平台化改造与模块切换
## 1. 项目概述
### 1.1 背景
当前站点是单功能"问题排查 AI 助手",访问 `/` 直接进入排查页面。需升级为多模块平台:新增平台首页展示模块卡片,问题排查助手降级为其中一个模块入口(`/troubleshoot`),后续可按需接入其他维护模块。
### 1.2 目标
| 目标编号 | 描述 | 优先级 |
|---------|------|--------|
| 1 | 模块注册机制 `modules.py` | 🟠 高 |
| 2 | 平台首页 `/`(模块卡片) | 🟠 高 |
| 3 | 排查助手路由迁移 `/``/troubleshoot` | 🟠 高 |
| 4 | 排查助手页加返回首页导航 | 🟡 中 |
### 1.3 开发周期
预估 5.5 小时(约 1 个工作日)。
---
## 2. 技术方案
### 2.1 模块注册机制(`utils/modules.py`,新增)
集中声明模块清单,提供 `get_modules(role=None)` 查询函数:
```python
"""modules.py — 平台模块注册
集中声明平台所有可用模块,供首页渲染和路由使用。
新增模块只需在此文件追加声明 + 注册 Blueprint,不改首页代码。
"""
from flask import session
# ============================================================
# 模块清单(声明式)
# ============================================================
MODULES = [
{
"id": "troubleshoot",
"name": "问题排查助手",
"icon": "🔍",
"description": "基于历史知识库的 AI 问题排查,357 条记录",
"url": "/troubleshoot",
"enabled": True,
"roles": [], # 空 = 所有登录用户可访问
"sort": 1,
},
{
"id": "service-monitor",
"name": "服务监控",
"icon": "📊",
"description": "服务器与服务运行状态监控、告警通知",
"url": "/service-monitor",
"enabled": False, # 预留,未上线
"roles": ["admin"],
"sort": 2,
},
# 后续模块在此追加
]
def get_modules(role=None):
"""获取可用模块列表。
参数:
role: 用户角色(None=不过滤,返回全部启用模块)
返回:
按 sort 排序的模块列表
"""
result = []
for m in MODULES:
if not m.get("enabled", True):
continue
if role is not None and m.get("roles") and role not in m["roles"]:
continue
result.append(m)
result.sort(key=lambda x: x.get("sort", 99))
return result
```
**关键约束**
- 模块声明集中在此文件,不散落各处
- `roles` 为空表示所有登录用户可访问(不限制)
- `enabled: False` 的模块不展示(预留但未上线)
### 2.2 平台首页路由(`routes/platform.py`,新增)
```python
"""platform.py — 平台首页路由"""
from flask import Blueprint, render_template, session, redirect, url_for
from decorators import page_login_required
from utils.modules import get_modules
bp = Blueprint('platform', __name__)
@bp.route('/')
def index():
"""平台首页 — 模块列表"""
user = session.get('user')
if not user:
return redirect(url_for('auth.login'))
role = user.get('role', '')
modules = get_modules(role=role)
return render_template('platform.html',
modules=modules,
user=user,
platform_name="运维辅助平台",
)
```
### 2.3 平台首页模板(`templates/platform.html`,新增)
独立模板,风格与现有 `index.html` / `login.html` 一致(渐变背景 + 卡片布局):
```html
<!-- platform.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5">
<title>{{ platform_name }}</title>
<style>
/* 复用 login/index 的设计系统变量 + 渐变背景 */
/* 模块卡片网格:桌面 3 列、平板 2 列、手机 1 列 */
/* 触控热区 ≥44px */
</style>
</head>
<body>
<!-- 顶部:平台标题 + 用户信息 + 退出 -->
<!-- 主体:模块卡片网格 -->
<!-- 每个卡片:图标 + 名称 + 描述 + "进入" 按钮 -->
<!-- 空状态:暂无可用模块 -->
</body>
</html>
```
### 2.4 排查助手路由迁移
#### 2.4.1 `routes/auth.py` 改动
`auth.py``index` 路由(`/` 重定向到 `/login` 或渲染 `index.html`)需移除或改为重定向到 `/troubleshoot`
实际上 `/` 现在由 `platform.py` 接管,`auth.py``index` 路由删除即可。
#### 2.4.2 `routes/troubleshoot.py` 改动
新增 `/troubleshoot` 页面路由(渲染 `index.html`):
```python
@bp.route('/troubleshoot')
def troubleshoot_page():
"""排查助手主页"""
return render_template('index.html')
```
#### 2.4.3 `templates/index.html` 改动
顶部用户信息栏增加"返回首页"链接:
```html
<a href="/" class="back-home">🏠 返回首页</a>
```
### 2.5 `server.py` 改动
注册 `platform` Blueprint:
```python
from routes.platform import bp as platform_bp
app.register_blueprint(platform_bp)
```
### 2.6 测试方案
| 模块 | 用例 | 覆盖点 |
|------|------|--------|
| `test_routes_auth.py` | 修改 | `/` 不再重定向到 `/login`(由 platform 接管) |
| `test_routes_troubleshoot.py` | 新增 | `GET /troubleshoot` 返回 200 |
| `test_routes_platform.py` | 新增 | `/` 未登录跳 `/login`;已登录返回模块卡片;角色过滤 |
| `test_modules.py` | 新增 | `get_modules()` 角色过滤 / enabled 过滤 / sort 排序 |
---
## 3. 实施计划
### 3.1 任务分解
| 序号 | 任务 | 预计时间 | 状态 | 依赖 |
|------|------|---------|------|------|
| 1 | `utils/modules.py` 模块注册 | 0.5h | 待开始 | — |
| 2 | `routes/platform.py` 平台首页路由 | 0.5h | 待开始 | 1 |
| 3 | `templates/platform.html` 平台首页模板 | 1.5h | 待开始 | 2 |
| 4 | 排查助手路由迁移 `/``/troubleshoot` | 0.5h | 待开始 | 2 |
| 5 | `templates/index.html` 加返回首页导航 | 0.5h | 待开始 | 4 |
| 6 | `server.py` 注册 platform Blueprint | 0.5h | 待开始 | 2 |
| 7 | 测试用例更新 + 新增 | 1h | 待开始 | 4, 5, 6 |
| 8 | 部署 + 验证 | 0.5h | 待开始 | 7 |
---
## 4. 测试验证
### 4.1 验证项
| 验收项 | 标准 | 实测 |
|--------|------|------|
| `/` 显示模块卡片 | 登录后看到"问题排查助手"卡片 | — |
| 卡片点击进入模块 | 跳转到 `/troubleshoot` | — |
| `/troubleshoot` 显示排查助手 | 原有功能全部正常 | — |
| 返回首页 | 排查助手页顶部有"返回首页"链接 | — |
| 角色过滤 | 普通用户/管理员看到各自模块 | — |
| 未登录 | `/``/login` | — |
| 移动端 | 首页卡片单列/多列自适应 | — |
| API 不变 | `/api/*` 全部正常 | — |
### 4.2 回归测试
- [ ] 145 用例全绿(路由变更需同步改测试 fixture)
- [ ] 排查助手全流程手动验证
### 4.3 端到端验证
```bash
# 1. 首页验证
curl -s http://192.168.5.60:8088/ -o /dev/null -w "%{http_code}"
# 期望:302(未登录跳 /login)
# 2. 登录后访问首页
# 期望:看到模块卡片
# 3. 排查助手验证
curl -s http://192.168.5.60:8088/api/health
# 期望:ok, 357 记录
# 4. 浏览器验证
# 登录 → 首页 → 点击"问题排查助手" → 排查页面 → 返回首页
```
---
## 5. 执行记录
### 5.1 执行日志
| 日期 | 任务 | 执行人 | 结果 | 备注 |
|------|------|--------|------|------|
| — | — | — | — | 待执行 |
### 5.2 问题记录
| 日期 | 问题 | 解决方案 | 状态 |
|------|------|---------|------|
| — | — | — | — |
---
## 6. 注意事项
1. **路由冲突**`platform.py` 注册 `/``auth.py` 原有 `/` 路由必须删除,否则 Flask 报 `AssertionError: duplicate route`
2. **Blueprint 注册顺序**`platform` Blueprint 需在 `auth` 之前注册(或确保 `/` 只定义一次)
3. **前端 API 路径不变**:排查助手 JS 中所有 `/api/...` 是绝对路径,不受主页路径迁移影响
4. **测试 fixture 更新**`conftest.py``app` fixture 创建的 test client 需适配新路由(`/` 不再是排查助手主页)
5. **部署清单同步**:新增 `modules.py` / `platform.py` / `platform.html` 需加入 `upload_to_server.py`
---
## 7. 相关文档
- [PRD_需求文档_平台化改造与模块切换](PRD_需求文档_平台化改造与模块切换.md) — 本项目需求文档
- [PRD_需求文档_P2级功能增强](PRD_需求文档_P2级功能增强.md) — P2 背景
# PRD 计划执行:服务管理模块
## 项目概述
### 背景
基于 `PRD_需求文档_服务管理模块.md`,实现服务管理模块的静态页面开发。参考 `https://192.168.5.44` 统一管理平台的设计风格,与现有 service-monitor 模块完全隔离。
### 目标
| 目标 | 说明 |
|------|------|
| 模块隔离 | 新模块 service-manage 与 service-monitor 物理隔离,互不影响 |
| 静态页面 | 实现三个页面:服务授权、服务升级、服务信息 |
| 权限控制 | 仅管理员可访问 |
| 响应式设计 | 适配桌面/平板/移动端 |
### 开发周期
- 预计总工时: 7h
- 开发周期: 1-2 天
---
## 技术方案
### 目录结构
```
skill/code/web/
├── service_manage/ # 新增目录
│ ├── __init__.py # Blueprint 暴露
│ └── routes.py # 路由定义
└── templates/service_manage/ # 新增目录
├── base.html # 基础模板(侧边栏 + 顶栏 + 内容区)
├── authorization.html # 服务授权页
├── upgrade.html # 服务升级页
└── info.html # 服务信息页
```
### Blueprint 定义
**文件**: `skill/code/web/service_manage/__init__.py`
```python
"""
service_manage — 服务管理模块(自包含子包)
对外仅暴露 Blueprint:
from service_manage import bp
app.register_blueprint(bp)
"""
from .routes import bp
__all__ = ["bp"]
```
**文件**: `skill/code/web/service_manage/routes.py`
```python
from flask import Blueprint, render_template
from web.decorators import page_login_required
bp = Blueprint('service-manage', __name__, url_prefix='/service-manage')
@bp.route('/')
@bp.route('/authorization')
@page_login_required
def authorization():
"""服务授权页面"""
return render_template('service_manage/authorization.html')
@bp.route('/upgrade')
@page_login_required
def upgrade():
"""服务升级页面"""
return render_template('service_manage/upgrade.html')
@bp.route('/info')
@page_login_required
def info():
"""服务信息页面"""
return render_template('service_manage/info.html')
```
### 模板继承关系
```
templates/base.html (主应用基础模板)
└── templates/service_manage/base.html (模块基础模板)
├── authorization.html
├── upgrade.html
└── info.html
```
### 样式方案
复用主应用 CSS(`/static/css/style.css`),并在 `service_manage/base.html` 中定义模块私有样式:
- 步骤卡片样式
- Tab 切换样式
- 服务列表表格样式
- 信息卡片样式
---
## 实施计划
### 阶段 1: 模块基础结构(1h)
| 序号 | 任务 | 预计时间 | 依赖 |
|------|------|----------|------|
| 1.1 | 创建 `skill/code/web/service_manage/` 目录 | 5min | - |
| 1.2 | 编写 `__init__.py`(Blueprint 暴露) | 10min | 1.1 |
| 1.3 | 编写 `routes.py`(路由定义) | 20min | 1.2 |
| 1.4 | 在 `server.py` 注册 Blueprint | 10min | 1.3 |
| 1.5 | 创建 `templates/service_manage/` 目录 | 5min | - |
| 1.6 | 编写 `base.html` 基础模板 | 10min | 1.5 |
### 阶段 2: 服务授权页面(2h)
| 序号 | 任务 | 预计时间 | 依赖 |
|------|------|----------|------|
| 2.1 | 编写 `authorization.html` 页面框架 | 30min | 1.6 |
| 2.2 | 实现 4 个步骤卡片布局 | 45min | 2.1 |
| 2.3 | 实现步骤 2 表单(5 个字段) | 30min | 2.2 |
| 2.4 | 样式调整和细节优化 | 15min | 2.3 |
### 阶段 3: 服务升级页面(2h)
| 序号 | 任务 | 预计时间 | 依赖 |
|------|------|----------|------|
| 3.1 | 编写 `upgrade.html` 页面框架 | 20min | 1.6 |
| 3.2 | 实现 Tab 切换功能(服务重启/服务回滚) | 30min | 3.1 |
| 3.3 | 实现顶部操作按钮栏 | 20min | 3.2 |
| 3.4 | 实现服务列表表格 | 40min | 3.3 |
| 3.5 | 样式调整和细节优化 | 10min | 3.4 |
### 阶段 4: 服务信息页面(1h)
| 序号 | 任务 | 预计时间 | 依赖 |
|------|------|----------|------|
| 4.1 | 编写 `info.html` 页面框架 | 15min | 1.6 |
| 4.2 | 实现监控类型下拉选择 | 15min | 4.1 |
| 4.3 | 实现 3 个信息卡片(CPU/内存/硬盘) | 20min | 4.2 |
| 4.4 | 样式调整和细节优化 | 10min | 4.3 |
### 阶段 5: 集成与验收(1h)
| 序号 | 任务 | 预计时间 | 依赖 |
|------|------|----------|------|
| 5.1 | 更新 `utils/modules.py` 模块元数据 | 10min | 1.4 |
| 5.2 | 验证三个页面路由正常 | 10min | 5.1 |
| 5.3 | 验证与 service-monitor 模块隔离 | 10min | 5.2 |
| 5.4 | 响应式布局测试和调整 | 20min | 5.3 |
| 5.5 | 管理员权限验证 | 10min | 5.4 |
---
## 测试验证
### 功能测试项
| 序号 | 测试项 | 预期结果 |
|------|--------|----------|
| 1 | 访问 `/service-manage/authorization` | 正确显示服务授权页面 |
| 2 | 访问 `/service-manage/upgrade` | 正确显示服务升级页面 |
| 3 | 访问 `/service-manage/info` | 正确显示服务信息页面 |
| 4 | 点击 Tab 切换(服务重启/服务回滚) | Tab 正确切换 |
| 5 | 服务升级页表格复选框全选 | 所有行被选中 |
| 6 | 响应式布局(移动端) | 页面自适应 |
### 隔离验证
| 序号 | 测试项 | 预期结果 |
|------|--------|----------|
| 1 | service-manage 代码不导入 service_monitor | 无交叉导入 |
| 2 | service-monitor 代码不导入 service_manage | 无交叉导入 |
| 3 | 两个模块数据目录独立 | 无共享数据 |
### 权限验证
| 序号 | 测试项 | 预期结果 |
|------|--------|----------|
| 1 | 未登录访问 `/service-manage/*` | 重定向到登录页 |
| 2 | 普通用户访问 `/service-manage/*` | 无权限提示(待定) |
| 3 | 管理员访问 `/service-manage/*` | 正常访问 |
---
## 执行记录
### 2026-07-22
| 时间 | 完成项 | 备注 |
|------|--------|------|
| - | 需求文档编写 | 完成 |
| - | 计划执行文档编写 | 完成 |
---
## 注意事项
### 隔离原则
- **代码隔离**: `service_manage/` 目录独立,不与 `service_monitor/` 共享代码
- **模板隔离**: `templates/service_manage/` 目录独立
- **数据隔离**: 暂无数据存储需求
### 样式复用
- 复用主应用 CSS,避免重复定义
- 遵循现有设计规范(颜色、字体、间距)
### 响应式设计
- 参考现有 `service_monitor` 的响应式实现
- 使用 CSS Grid/Flexbox 布局
---
## 相关文档
- [[PRD_需求文档_服务管理模块]]
- [[PRD_需求文档_服务监测模块]]
- [[PRD_计划执行_平台化改造与模块切换]]
\ No newline at end of file
# PRD 计划执行文档 — 返回首页导航栏改造 + 新增服务管理/服务监测模块
> 版本:1.0 | 日期:2026-07-14 | 分支:troubleshoot-ai-assistant
---
## 执行步骤
### 步骤 1:修改 modules.py — 新增模块声明
**文件**`skill/code/web/utils/modules.py`
**操作**:在 `MODULES` 列表中追加两个模块声明
```python
{
"id": "service-manage",
"name": "服务管理",
"icon": "🛠️",
"description": "新统一平台服务授权、服务升级、服务信息管理",
"url": "/service-manage",
"enabled": True,
"roles": ["admin"],
"sort": 2,
},
{
"id": "service-monitor",
"name": "服务监测",
"icon": "📊",
"description": "通过 SSH 连接监测服务器服务运行状态、输出报告",
"url": "/service-monitor",
"enabled": True,
"roles": [],
"sort": 3,
},
```
**注意**:原 `service-monitor`(enabled:False, roles:["admin"], sort:2)替换为新声明。
---
### 步骤 2:新建服务管理占位路由
**新建文件**`skill/code/web/routes/service_manage.py`
- Blueprint: `service-manage`
- 路由: `@bp.route('/service-manage')`
- 装饰器: `@page_login_required`
- 渲染: `service_manage.html`,传入 `user=session.get('user', {})`
---
### 步骤 3:新建服务管理占位模板
**新建文件**`skill/code/web/templates/service_manage.html`
- 顶部导航栏:`🛠️ 运行维护平台` + `/ 服务管理` + 用户名 + 退出
- 主体:`🚧 服务管理模块开发中` + 计划功能列表(服务授权/服务升级/服务信息)+ 返回首页按钮
- 移动端响应式
---
### 步骤 4:新建服务监测占位路由
**新建文件**`skill/code/web/routes/service_monitor.py`
- Blueprint: `service-monitor`
- 路由: `@bp.route('/service-monitor')`
- 装饰器: `@page_login_required`
- 渲染: `service_monitor.html`,传入 `user=session.get('user', {})`
---
### 步骤 5:新建服务监测占位模板
**新建文件**`skill/code/web/templates/service_monitor.html`
- 顶部导航栏:`🛠️ 运行维护平台` + `/ 服务监测` + 用户名 + 退出
- 主体:`🚧 服务监测模块开发中` + 计划功能列表(SSH连接监测/服务状态监测/报告输出)+ 返回首页按钮
- 移动端响应式
---
### 步骤 6:改造 index.html 顶部导航栏
**文件**`skill/code/web/templates/index.html`
**操作**
1.`<style>` 中添加 `.navbar` / `.navbar-left` / `.navbar-brand` / `.navbar-module` / `.navbar-user` / `.navbar-logout` 样式
2. 在移动端 `@media` 中添加导航栏适配(面包屑隐藏、触控热区 ≥44px)
3.`<body>` 后、`<div class="container">` 前插入导航栏 HTML
4. 删除 card 内部的 `user-info-bar` div(返回首页 + 用户名 + 退出已移到导航栏)
5. 删除移动端中已无用的 `.user-info` 样式
**导航栏结构**
```html
<div class="navbar">
<div class="navbar-left">
<a href="/" class="navbar-brand">🛠️ 运行维护平台</a>
<span class="navbar-module">/ 问题排查助手</span>
</div>
<div class="navbar-user">
<span id="userDisplayName">加载中...</span>
<button class="navbar-logout" onclick="logout()">🚪 退出</button>
</div>
</div>
```
---
### 步骤 7:server.py 注册新 Blueprint
**文件**`skill/code/web/server.py`
**操作**:在 `create_app()` 中注册两个新 Blueprint
```python
from routes.service_manage import bp as service_manage_bp
from routes.service_monitor import bp as service_monitor_bp
app.register_blueprint(service_manage_bp)
app.register_blueprint(service_monitor_bp)
```
---
### 步骤 8:部署清单确认
**文件**`deploy/upload_to_server.py`
**无需修改**`routes/``templates/` 目录已在 `DIRS_TO_UPLOAD` 中,新文件自动包含。
---
### 步骤 9:测试验证
1. `cd skill/code && python -m pytest -v` — 145 用例全绿
2. Chrome DevTools 验证:
- `/` 平台首页显示 3 个模块卡片(问题排查助手 + 服务管理 + 服务监测)
- 点击"服务管理"进入占位页,顶部导航栏可返回首页
- 点击"服务监测"进入占位页,顶部导航栏可返回首页
- 问题排查助手顶部导航栏可返回首页
3. 部署到 5.60 + `verify_deployment.py`
---
## 变更文件清单
| 文件 | 操作 | 说明 |
|------|------|------|
| `skill/code/web/utils/modules.py` | 修改 | 新增 service-manage + service-monitor 声明 |
| `skill/code/web/routes/service_manage.py` | **新建** | 服务管理占位路由 |
| `skill/code/web/routes/service_monitor.py` | **新建** | 服务监测占位路由 |
| `skill/code/web/templates/service_manage.html` | **新建** | 服务管理占位页面 |
| `skill/code/web/templates/service_monitor.html` | **新建** | 服务监测占位页面 |
| `skill/code/web/templates/index.html` | 修改 | 返回首页改为顶部导航栏 |
| `skill/code/web/server.py` | 修改 | 注册新 Blueprint |
# PRD_需求文档_平台化改造与模块切换
## 基本信息
| 项目 | 内容 |
|------|------|
| 文档类型 | 需求文档 |
| 创建日期 | 2026-07-14 |
| 最后更新 | 2026-07-14 |
| 负责人 | 研发组(Claude 协助) |
| 优先级 | P2 🟠 |
| 状态 | 待开始 |
---
## 一、背景与目标
### 1.1 问题背景
当前「问题排查 AI 助手」是一个独立单功能站点:访问 `/` 直接进入排查助手主页面。平台后续将承载**多个维护类模块**(不仅限于问题知识助手),需要把现有站点升级为一个可扩展的**多模块平台**,问题排查助手降级为其中一个模块。
| 序号 | 现状 | 问题 |
|------|------|------|
| 1 | `/` 直接是排查助手主页 | 无法承载多模块,新模块无入口 |
| 2 | 站点定位是"问题排查助手" | 与"运维平台"定位不符,扩展性差 |
| 3 | 无模块切换机制 | 多模块并存时用户无法在模块间导航 |
### 1.2 修复目标
1. **新增平台首页** `/`:作为统一入口,以卡片/列表形式展示所有可用模块,点击进入对应模块
2. **问题排查助手降级为模块**:原 `/` 主页迁移到 `/troubleshoot`,首页改为模块列表
3. **预留服务监控模块**:在模块清单中声明"服务监控"模块(`enabled: false`,仅管理员可见),为后续接入预留入口
4. **建立模块注册机制**:通过配置声明模块清单,首页动态渲染,便于后续接入新模块
5. **共用服务与认证**:所有模块共用同一 Flask 服务、用户认证、缓存管理,各模块独立路由与页面
6. **保持现有功能零回归**:问题排查助手的搜索/AI 分析/提交/导出/缓存管理等全部功能不变,仅路径调整
### 1.3 范围说明
- 本次**只建框架**,先接入"问题排查助手"一个模块,**预留"服务监控"模块**`enabled: false`),后续按需接入
- 不涉及微服务拆分,全部在现有 Flask 服务内通过路由隔离实现
---
## 二、需求详情
### 2.1 模块注册机制
#### 2.1.1 需求规格
平台通过配置声明模块清单,首页动态渲染卡片。模块清单字段:
| 字段 | 说明 | 示例 |
|------|------|------|
| `id` | 模块唯一标识 | `troubleshoot` |
| `name` | 模块显示名 | `问题排查助手` |
| `icon` | 模块图标(emoji 或图标类) | `🔍` |
| `description` | 模块一句话描述 | `基于历史知识库的 AI 问题排查` |
| `url` | 模块入口路径 | `/troubleshoot` |
| `enabled` | 是否启用 | `true` |
| `roles` | 允许访问的角色(空=所有登录用户) | `[]` |
| `sort` | 排序权重 | `1` |
#### 2.1.2 实现方式
-`config.json` 新增 `modules` 数组,或新建 `utils/modules.py` 集中声明(推荐后者,便于扩展)
- 首页通过 `get_modules()` 读取清单并渲染卡片
#### 2.1.3 验收标准
- [ ] 模块清单可通过配置增删
- [ ] `enabled: false` 的模块不在首页展示
- [ ] 模块按 `sort` 排序展示
---
### 2.2 平台首页(新增)
#### 2.2.1 需求规格
| 项 | 规格 |
|----|------|
| 路径 | `/`(覆盖原排查助手主页) |
| 访问控制 | 登录后可见(未登录跳 `/login`) |
| 页面内容 | 顶部:平台标题 + 用户信息 + 退出;主体:模块卡片网格 |
| 卡片内容 | 图标 + 模块名 + 描述 + "进入"按钮 |
| 卡片点击 | 跳转到模块 `url` |
| 角色过滤 | 按 `modules[].roles` 过滤当前用户可见模块 |
| 响应式 | 移动端单列、桌面多列(沿用 P2-2 断点策略) |
| 空状态 | 无可用模块时显示"暂无可用模块"提示 |
#### 2.2.2 验收标准
- [ ] 访问 `/` 显示模块列表(非排查助手主页)
- [ ] 未登录跳转 `/login`
- [ ] 卡片点击进入对应模块
- [ ] 移动端响应式正常
- [ ] 普通用户/管理员看到各自有权限的模块
---
### 2.3 问题排查助手降级为模块
#### 2.3.1 路径调整
| 原路径 | 新路径 | 说明 |
|--------|--------|------|
| `/`(排查助手主页) | `/troubleshoot` | 排查助手主页迁移 |
| `/login` `/logout` | `/login` `/logout` | 不变(平台级认证) |
| `/api/*` | `/api/*` | 不变(API 路径保持) |
#### 2.3.2 需求规格
- 排查助手主页路由从 `/` 改为 `/troubleshoot`
- 排查助手页面顶部增加"返回平台首页"入口(链接到 `/`
- 排查助手所有功能(搜索/分析/提交/导出/缓存)**零变更**
- 前端 JS 中的 API 调用路径(相对路径 `/api/...`**无需改**
#### 2.3.3 验收标准
- [ ] `/troubleshoot` 正常显示排查助手主页
- [ ] `/` 显示平台首页(非排查助手)
- [ ] 排查助手全部功能正常(搜索/AI 分析/提交/导出/缓存)
- [ ] 排查助手页面有返回首页入口
---
### 2.4 平台导航
#### 2.4.1 需求规格
- 排查助手(及后续模块)页面顶部增加统一导航:平台名 + "返回首页"链接
- 首页顶部:平台标题 + 用户信息 + 退出(沿用现有用户信息栏样式)
#### 2.4.2 验收标准
- [ ] 各模块页可一键返回平台首页
- [ ] 平台标题一致
---
## 三、影响范围
### 3.1 涉及文件
| 文件 / 目录 | 变更类型 | 说明 |
|------------|---------|------|
| `web/utils/modules.py` | 新增 | 模块清单声明 + `get_modules()` |
| `web/routes/platform.py` | 新增 | 平台首页路由 `/` |
| `web/routes/auth.py` | 修改 | `/` 重定向改为 `/troubleshoot` 之外的处理(或交由 platform 路由) |
| `web/routes/troubleshoot.py` 或 server.py | 修改 | 排查助手主页路由 `/``/troubleshoot` |
| `web/templates/platform.html` | 新增 | 平台首页模板(模块卡片) |
| `web/templates/index.html` | 修改 | 顶部加"返回首页"导航 |
| `web/config.json` | 修改 | 可选:模块配置(若用配置驱动) |
| `deploy/upload_to_server.py` | 修改 | 上传清单补新文件 |
### 3.2 风险评估
| 风险项 | 等级 | 缓解措施 |
|--------|------|---------|
| 原 `/` 路径行为变化,外部书签失效 | 🟠 中 | `/troubleshoot` 与原 `/` 行为一致;老用户从首页点入即可 |
| 排查助手前端 JS 路径依赖 | 🟡 低 | API 用相对路径 `/api/...`,不随主页路径变化 |
| 认证流程受影响 | 🟡 低 | `/login` 不变,首页加 `@page_login_required` |
| 移动端首页布局 | 🟡 低 | 复用 P2-2 断点策略 |
---
## 四、非功能需求
| 项 | 要求 |
|----|------|
| 扩展性 | 新增模块只需在 `modules.py` 声明 + 注册路由,不改首页代码 |
| 兼容性 | 排查助手功能零回归;现有测试用例保持全绿 |
| 性能 | 首页轻量,无额外 API 调用(模块清单内存读取) |
| 安全 | 首页需登录;模块按角色过滤 |
| 可维护性 | 模块声明集中管理,不散落各处 |
---
## 五、时间估算
| 任务 | 工时 |
|------|------|
| 模块注册机制 `modules.py` | 0.5h |
| 平台首页路由 + 模板 | 2h |
| 排查助手路由迁移 `/``/troubleshoot` | 0.5h |
| 排查助手页加返回首页导航 | 0.5h |
| 移动端适配首页 | 1h |
| 测试 + 部署 | 1h |
| **合计** | **5.5h** |
---
## 六、验收清单
### 模块注册
- [ ] `modules.py` 声明模块清单
- [ ] `get_modules()` 支持角色过滤
### 平台首页
- [ ] `/` 显示模块卡片列表
- [ ] 未登录跳 `/login`
- [ ] 卡片点击进入模块
- [ ] 移动端响应式
### 排查助手降级
- [ ] `/troubleshoot` 显示排查助手主页
- [ ] 排查助手全部功能正常
- [ ] 页面有返回首页入口
### 测试与部署
- [ ] 现有 145 用例全绿(路由变更需同步改测试)
- [ ] 部署到 5.60 验证
---
## 维护记录
| 日期 | 更新内容 | 更新人 |
|------|---------|--------|
| 2026-07-14 | 初版创建 | 研发组 |
# PRD 需求文档:服务管理模块
## 基本信息
| 项目 | 内容 |
|------|------|
| 文档类型 | PRD 需求文档 |
| 所属模块 | 服务管理(service-manage) |
| 创建日期 | 2026-07-22 |
| 负责人 | czj |
| 优先级 | P1 |
| 状态 | 开发中 |
| 关联分支 | troubleshoot-ai-assistant |
## 背景与目标
### 背景
当前项目已实现"服务监测"模块(service-monitor),用于监测服务器/服务状态并输出报告。现需要开发配套的"服务管理"模块(service-manage),提供服务授权管理、服务升级管理、服务信息查看等功能。
### 目标
1. 参考 `https://192.168.5.44` 统一管理平台的模块设计,实现静态页面
2. 与现有"服务监测"模块完全隔离,互不影响
3. 仅管理员可访问,确保安全性
### 非目标
- 本次仅开发静态页面,不实现后端业务逻辑
- 不涉及与外部系统的集成(如授权文件生成、服务重启等)
## 需求详情
### 1. 服务授权页面(Service Authorization)
**页面路径**: `/service-manage/authorization`
**功能描述**: 服务授权申请流程,分4个步骤卡片展示
#### 步骤 1: 申请服务授权
- 卡片标题:"1. 申请服务授权"
- 操作按钮:
- "下载激活文件" 按钮
- "上传授权文件" 按钮(文件上传控件)
#### 步骤 2: 填写项目信息
- 卡片标题:"2. 填写项目信息"
- 表单字段(带必填标记 *):
- 项目销售(文本输入框)
- 部署人员(文本输入框)
- 下单时间(日期时间选择器)
- 服务器密码(密码输入框)
- 项目概述(多行文本框)
- 操作按钮:"保存" 按钮
#### 步骤 3: 填写并提交部署视图
- 卡片标题:"3. 填写并提交部署视图"
- 操作按钮:"下载部署视图" 按钮
#### 步骤 4: 填写并提交自检文档
- 卡片标题:"4. 填写并提交自检文档"
- 操作按钮:"下载自检文档" 按钮
**验收标准**:
- [ ] 页面正确显示4个步骤卡片
- [ ] 表单字段布局合理,标签清晰
- [ ] 按钮样式与平台整体风格一致
- [ ] 响应式布局适配移动端
---
### 2. 服务升级页面(Service Upgrade)
**页面路径**: `/service-manage/upgrade`
**功能描述**: 服务重启/回滚管理,Tab 切换 + 服务列表表格
#### 顶部标签页
- Tab 1: "服务重启"(默认选中)
- Tab 2: "服务回滚"
#### 顶部操作栏
- "下载配置" 按钮
- "备份数据库文件" 按钮
- "上传更新服务" 按钮(文件上传)
- "重启已选服务" 按钮(批量操作,默认禁用)
- "下载日志" 按钮(批量操作,默认禁用)
#### 服务列表表格
| 列名 | 说明 |
|------|------|
| ☐ | 全选复选框 |
| 服务名称 | 如"运维系统"、"预定系统2.0" |
| 服务类型 | 如"PYTHON"、"JAVA" |
| 升级路径 | 服务部署路径 |
| 重启脚本路径 | 启动脚本路径 |
| 操作 | "重启"按钮、"下载日志"按钮 |
**验收标准**:
- [ ] Tab 切换功能正常
- [ ] 表格正确渲染,列宽合理
- [ ] 复选框可选择/取消
- [ ] 批量操作按钮根据选择状态启用/禁用
- [ ] 响应式布局适配移动端
---
### 3. 服务信息页面(Service Info)
**页面路径**: `/service-manage/info`
**功能描述**: 服务监控信息概览
#### 筛选区域
- 监控类型下拉选择框(默认值:"system")
#### 信息卡片区域
- CPU 使用率(进度条/数值展示)
- 内存使用率(进度条/数值展示)
- 硬盘使用率(进度条/数值展示)
**验收标准**:
- [ ] 下拉选择框功能正常
- [ ] 信息卡片布局清晰
- [ ] 进度条/数值展示直观
---
## 页面布局设计
### 通用布局
- 继承 `base.html` 模板
- 顶部导航栏:显示当前模块路径(如"系统管理 / 服务授权")
- 左侧菜单栏:
- 服务授权(链接到 `/service-manage/authorization`
- 服务升级(链接到 `/service-manage/upgrade`
- 服务信息(链接到 `/service-manage/info`
### 顶部 Tab 导航
- 三个一级菜单作为 Tab:
- 服务授权
- 服务升级
- 服务信息
- 点击 Tab 切换到对应页面
---
## 技术方案
### 模块隔离方案
参考 `service_monitor` 的隔离模式:
```
skill/code/web/
├── service_manage/ # 新增:服务管理模块(自包含子包)
│ ├── __init__.py # 仅暴露 Blueprint
│ ├── routes.py # 路由层:页面路由
│ └── templates/ # 模板(可选,或复用主 templates/)
└── templates/service_manage/ # 模板目录
├── base.html # 基础模板
├── authorization.html # 服务授权页
├── upgrade.html # 服务升级页
└── info.html # 服务信息页
```
### Blueprint 注册
- Blueprint 名称: `service-manage`
- URL 前缀: `/service-manage`
- 权限控制: `@page_login_required` + 管理员权限检查
### 模板继承
- 创建 `templates/service_manage/base.html` 作为模块基础模板
- 子模板继承 base.html,填充 `{% block content %}`
---
## 影响范围
| 文件 | 变更类型 | 说明 |
|------|----------|------|
| `skill/code/web/service_manage/__init__.py` | 新增 | Blueprint 定义 |
| `skill/code/web/service_manage/routes.py` | 新增 | 页面路由 |
| `skill/code/web/templates/service_manage/base.html` | 新增 | 基础模板 |
| `skill/code/web/templates/service_manage/authorization.html` | 新增 | 服务授权页 |
| `skill/code/web/templates/service_manage/upgrade.html` | 新增 | 服务升级页 |
| `skill/code/web/templates/service_manage/info.html` | 新增 | 服务信息页 |
| `skill/code/web/server.py` | 修改 | 注册新 Blueprint |
| `skill/code/web/utils/modules.py` | 修改 | 更新模块元数据 |
---
## 非功能需求
| 项目 | 要求 |
|------|------|
| 性能 | 静态页面加载时间 < 500ms |
| 兼容性 | 支持 Chrome、Edge、Firefox 最新版本 |
| 响应式 | 适配桌面(>1200px)、平板(768-1200px)、移动端(<768px) |
| 可访问性 | 符合 WCAG 2.1 AA 标准 |
---
## 时间估算
| 任务 | 预计时间 |
|------|----------|
| 创建模块目录结构和 Blueprint | 0.5h |
| 开发基础模板 (base.html) | 1h |
| 开发服务授权页面 | 2h |
| 开发服务升级页面 | 2h |
| 开发服务信息页面 | 1h |
| 注册 Blueprint 和模块元数据更新 | 0.5h |
| 响应式适配和样式调整 | 1h |
| **总计** | **7h** |
---
## 验收清单
- [ ] 三个页面均可正常访问
- [ ] 页面布局与参考平台一致
- [ ] Tab 切换功能正常
- [ ] 表单和按钮样式统一
- [ ] 响应式布局正常
- [ ] 与 service-monitor 模块完全隔离,无交叉影响
- [ ] 仅管理员可访问
---
## 附录
### 参考链接
- 参考平台: `https://192.168.5.44/#/backend/backstage`
- 账号: superadmin / Ubains@1357
### 相关文档
- [[PRD_需求文档_服务监测模块]]
- [[PRD_计划执行_平台化改造与模块切换]]
\ No newline at end of file
# PRD 需求文档 — 返回首页导航栏改造 + 新增服务管理/服务监测模块
> 版本:1.0 | 日期:2026-07-14 | 分支:troubleshoot-ai-assistant
---
## 1. 需求背景
### 1.1 返回首页问题
当前问题排查助手模块(`/troubleshoot`)的"返回首页"链接嵌在用户信息栏内,样式为蓝色小文字按钮,不够显眼,用户反馈"无法返回主页"。
**目标**:将返回首页改为顶部导航栏形式,与平台首页(`/`)风格统一,确保跨模块导航清晰可见。
### 1.2 新增模块
平台首页当前仅有"问题排查助手"一个模块卡片,需要新增两个模块占位:
- **服务管理**:用于新统一平台的服务授权、服务升级、服务信息管理(本期仅占位)
- **服务监测**:用于通过 SSH 连接监测服务器服务运行状态、输出报告(本期仅占位)
---
## 2. 功能需求
### 2.1 顶部导航栏改造
| 需求项 | 说明 |
|--------|------|
| 导航栏位置 | 页面顶部,card 外部上方 |
| 导航栏风格 | 与平台首页统一:渐变背景 + 毛玻璃效果 + 白色文字 |
| 左侧内容 | `🛠️ 运行维护平台` 品牌链接(href="/")+ `/ 模块名` 面包屑 |
| 右侧内容 | 用户名(角色)+ 退出按钮 |
| 移动端适配 | 面包屑隐藏,退出按钮 ≥44px 触控热区 |
| 影响范围 | `index.html`(问题排查助手页面) |
### 2.2 服务管理模块(占位)
| 需求项 | 说明 |
|--------|------|
| 模块 ID | `service-manage` |
| 模块名称 | 服务管理 |
| 图标 | 🛠️ |
| 描述 | 新统一平台服务授权、服务升级、服务信息管理 |
| URL | `/service-manage` |
| 访问权限 | 仅管理员(`roles: ["admin"]`) |
| 页面内容 | 占位页面:顶部导航栏 + "🚧 服务管理模块开发中" + 计划功能列表 + 返回首页按钮 |
| 本期实现 | 仅占位卡片 + 静态页面,不实现具体功能 |
### 2.3 服务监测模块(占位)
| 需求项 | 说明 |
|--------|------|
| 模块 ID | `service-monitor` |
| 模块名称 | 服务监测 |
| 图标 | 📊 |
| 描述 | 通过 SSH 连接监测服务器服务运行状态、输出报告 |
| URL | `/service-monitor` |
| 访问权限 | 所有登录用户(`roles: []`) |
| 页面内容 | 占位页面:顶部导航栏 + "🚧 服务监测模块开发中" + 计划功能列表 + 返回首页按钮 |
| 本期实现 | 仅占位卡片 + 静态页面,不实现具体功能 |
---
## 3. 非功能需求
| 项目 | 要求 |
|------|------|
| 移动端适配 | 所有新增页面需响应式适配(≤768px 手机、≤1024px 平板) |
| 导航一致性 | 所有模块页面顶部导航栏风格统一 |
| 单元测试 | 现有 145 用例不受影响,全绿 |
| 部署 | 更新到 192.168.5.60:8088 |
---
## 4. 约束
- 服务管理/服务监测本期**仅占位**,不实现具体功能
- 不修改 `search_engine.py` / `safety_filter.py` / `cache_manager.py` 公开接口
- 新增模块遵循现有 routes/services/utils 三层架构
......@@ -27,7 +27,8 @@ from services.service_manage import (
update_target, delete_target, test_connection,
save_project, get_project,
get_activation_file, get_deploy_view_file, get_self_check_file,
save_uploaded_file,
save_uploaded_file, upload_license_file, check_license_task,
_upload_to_target,
)
logger = logging.getLogger("service_manage.routes")
......@@ -184,7 +185,7 @@ def license_download():
@bp.route('/api/service-manage/license/upload', methods=['POST'])
def license_upload():
"""上传授权文件(暂存,后续代理转发到目标服务器)。"""
"""上传授权文件(代理到目标服务器;无目标时本地暂存)。"""
guard = _require_admin_json()
if guard:
return guard
......@@ -195,16 +196,9 @@ def license_upload():
if not file_storage.filename:
return jsonify({"success": False, "error": {"code": 400, "message": "文件名为空"}}), 400
result = save_uploaded_file(file_storage, subdir="license")
_audit("upload_license", f"上传授权文件: {result['filename']}")
# 阶段一:本地暂存后直接返回成功(未来对接目标服务器后返回真实 taskId)
return jsonify({
"success": True,
"taskId": f"local-{result['filename']}",
"status": "pending",
"message": "授权文件已上传,处理中",
})
result = upload_license_file(file_storage)
_audit("upload_license", f"上传授权文件: {result.get('filename', '')}")
return jsonify(result)
except Exception as e:
logger.exception("上传授权文件失败")
return jsonify({"success": False, "error": {"code": 500, "message": str(e)}}), 500
......@@ -212,18 +206,18 @@ def license_upload():
@bp.route('/api/service-manage/license/task-status', methods=['GET'])
def license_task_status():
"""查询授权任务状态(阶段一:本地模式直接返回完成)。"""
"""查询授权任务状态(代理到目标服务器;本地模式直接返回完成)。"""
guard = _require_admin_json()
if guard:
return guard
# 阶段一:本地暂存模式,任务立即完成
# 后续对接目标服务器后,需要轮询目标服务器的真实状态
return jsonify({
"success": True,
"taskId": request.args.get("taskId", ""),
"status": "completed",
"message": "授权处理完成",
})
try:
task_id = request.args.get("taskId", "")
result = check_license_task(task_id)
result["taskId"] = task_id
return jsonify(result)
except Exception as e:
logger.exception("查询授权任务状态失败")
return jsonify({"success": False, "error": {"code": 500, "message": str(e)}}), 500
@bp.route('/api/service-manage/project', methods=['GET', 'POST'])
......@@ -274,7 +268,7 @@ def deploy_view_download():
@bp.route('/api/service-manage/deploy-view/upload', methods=['POST'])
def deploy_view_upload():
"""上传部署视图。"""
"""上传部署视图(代理到目标服务器;无目标时本地暂存)。"""
guard = _require_admin_json()
if guard:
return guard
......@@ -285,9 +279,9 @@ def deploy_view_upload():
if not file_storage.filename:
return jsonify({"success": False, "error": {"code": 400, "message": "文件名为空"}}), 400
result = save_uploaded_file(file_storage, subdir="deploy-view")
_audit("upload_deploy_view", f"上传部署视图: {result['filename']}")
return jsonify({"success": True, "message": "部署视图上传成功", "file": result})
result = _upload_to_target(file_storage, "upload_deploy_view")
_audit("upload_deploy_view", f"上传部署视图: {file_storage.filename}")
return jsonify(result)
except Exception as e:
logger.exception("上传部署视图失败")
return jsonify({"success": False, "error": {"code": 500, "message": str(e)}}), 500
......@@ -315,7 +309,7 @@ def self_check_download():
@bp.route('/api/service-manage/self-check/upload', methods=['POST'])
def self_check_upload():
"""上传自检文档。"""
"""上传自检文档(代理到目标服务器;无目标时本地暂存)。"""
guard = _require_admin_json()
if guard:
return guard
......@@ -326,9 +320,9 @@ def self_check_upload():
if not file_storage.filename:
return jsonify({"success": False, "error": {"code": 400, "message": "文件名为空"}}), 400
result = save_uploaded_file(file_storage, subdir="self-check")
_audit("upload_self_check", f"上传自检文档: {result['filename']}")
return jsonify({"success": True, "message": "自检文档上传成功", "file": result})
result = _upload_to_target(file_storage, "upload_self_check")
_audit("upload_self_check", f"上传自检文档: {file_storage.filename}")
return jsonify(result)
except Exception as e:
logger.exception("上传自检文档失败")
return jsonify({"success": False, "error": {"code": 500, "message": str(e)}}), 500
\ No newline at end of file
此差异已折叠。
......@@ -186,6 +186,37 @@
font-size: 12px;
}
/* ===== Toast 通知 ===== */
#toast-container {
position: fixed;
top: 20px;
right: 20px;
z-index: 9999;
display: flex;
flex-direction: column;
gap: 10px;
max-width: 360px;
}
.toast {
background: #fff;
border-radius: 8px;
padding: 12px 16px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
border-left: 4px solid var(--primary);
font-size: 14px;
color: var(--gray-900);
opacity: 0;
transform: translateX(20px);
transition: opacity 0.3s, transform 0.3s;
display: flex;
align-items: center;
gap: 8px;
}
.toast.show { opacity: 1; transform: translateX(0); }
.toast.toast-success { border-left-color: var(--green); }
.toast.toast-error { border-left-color: var(--red); }
.toast.toast-warning { border-left-color: var(--yellow); }
/* ===== 移动端响应式 ===== */
@media screen and (max-width: 768px) {
.sidebar {
......@@ -253,6 +284,9 @@
{% block content %}{% endblock %}
</main>
<!-- Toast 容器 -->
<div id="toast-container"></div>
<div class="footer">运行维护平台 · 服务管理模块</div>
<script>
......@@ -281,6 +315,24 @@
} catch (e) {}
window.location.href = '/login';
}
// Toast 通知
function showToast(message, type) {
type = type || 'success';
var container = document.getElementById('toast-container');
var toast = document.createElement('div');
toast.className = 'toast toast-' + type;
var icons = {success: '✅', error: '❌', warning: '⚠️'};
toast.innerHTML = (icons[type] || 'ℹ️') + ' ' + message;
container.appendChild(toast);
requestAnimationFrame(function() {
toast.classList.add('show');
});
setTimeout(function() {
toast.classList.remove('show');
setTimeout(function() { toast.remove(); }, 300);
}, 3000);
}
</script>
{% block extra_js %}{% endblock %}
</body>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论