功能:补充固定场景助手与GPU健康诊断能力

This commit is contained in:
songyiming committed 2026-11-12 17:33:07 +08:00
1 parent 9f05aa20ea
commit b6026f9759
29 files changed
+1333 -22

No files matched your search

+2
View File
@@ -18,6 +18,8 @@
[高级版日志与诊断中心](cloud-diagnostics-20261111.md):按设备/桌面关联任务、会话、告警和审计,设备日志采集回执与完整诊断包导出;已完成真实盒子采集与页面验收。
[通用版固定场景助手向导](cloud-assistant-wizard-20261112.md):先不接模型,通用工作台可生成并确认真实员工授权方案;未知对象和未支持场景会阻止写操作,已部署到 5182。
[云管开发说明](cloud-platform-development.md)记录第一阶段代码、启动方式、接口与当前限制;本机控制台使用 Go API 和 PostgreSQL;已另行完成[真实 PVE 与盒子接入](cloud-live-integration.md),正式入口为 5182。
[控制台 UI 接入记录](cloud-console-ui-20261109.md)记录按 `ui/` 设计稿接入的深浅主题、页面范围、验证方式与实际数据边界。
+26
View File
@@ -0,0 +1,26 @@
# 通用版固定场景助手向导
日期:2026-11-12。第一阶段先实现不依赖模型的固定场景向导,入口为通用版「工作台」。
## 当前能力
工作台输入一句简短描述后生成方案卡片:
- “给张三分配备用桌面”这类请求会按员工姓名/账号和桌面名称/VMID匹配真实对象。
- 方案展示员工、桌面、影响范围和执行边界;只追加授权,不新建 VM、不自动开机、不终止已有连接。
- 点击执行后仍需确认,服务端复用现有 `POST /users/batch-bindings`,执行结果来自真实授权接口。
- 员工不存在、同名无法唯一匹配、授权已经存在或没有可分配桌面时,只显示阻止原因,不发写请求。
- “桌面打不开/设备离线”进入问题处理;“异常/告警/问题”进入操作记录。
- viewer 和 operator 即使能看到工作台,也继续由后端 RBAC 控制;执行授权仍需超级管理员。
本阶段没有接入语言模型,没有让自然语言直接变成 Shell、PowerShell、SSH 或电源命令。未知句式会明确提示当前支持的固定场景。
## 验证
- TypeScript、ESLint、生产构建通过。
- 通用版浏览器回归 15/15 通过,新增覆盖:可执行授权方案、确认后调用授权接口、未知员工阻止、已有授权阻止、同名桌面阻止及无写请求。
- 既有 6 项前端单元测试通过。
已发布到真实 `http://127.0.0.1:5182`。本次仅更新控制台静态镜像;API、数据库表、管理员账号、终端、桌面和 PVE 配置没有变更。真实验收使用临时员工和已有桌面完成了一次页面授权,随后定向清理,临时员工和绑定均为 0。
后续可在同一方案对象上增加批量电源预检和终端无桌面诊断,再评估模型服务;模型必须只能选择受控业务动作,不能直接执行自由文本命令。
+2
View File
@@ -155,3 +155,5 @@ Go 全量测试与 `go vet` 通过,使用独立临时 PostgreSQL,覆盖账
11-11 追加:[通用/高级两版界面](cloud-editions-20261111.md)已在 5182 部署,包含通用员工授权向导、设备登记/诊断、简明记录和高级显卡准备结果核对。两版共用真实 API/RBAC;首批 34 项页面测试、6 项单元测试和 GPU 专项集成通过,真实临时员工授权流程验收后已清理。模型暂不接入,剩余高级事项保持原验收边界。
11-11 追加:[高级版日志与诊断中心](cloud-diagnostics-20261111.md)已在 5182 部署:设备/桌面按唯一 ID 关联五类记录,数据库分页、来源/时间筛选、设备采集回执和完整 ZIP 导出。6 项后端专项、Go 全量/vet、25 项浏览器回归及真实接口/页面验收通过;真实盒子成功完成一次只读日志采集。现有身份、授权、连接配置及告警规则保持,唯一新增任务为该次采集。持续实时日志、全量归档和客体自动采集仍待后续。
12-11 追加:[通用版固定场景助手向导](cloud-assistant-wizard-20261112.md)已完成代码和隔离页面验收并部署到 5182:输入员工与桌面需求生成授权方案,确认后复用现有绑定接口;未知对象、重复授权、同名桌面和未支持句式不发写请求。15 项通用版浏览器测试、TypeScript、ESLint、单元测试和生产构建通过;真实临时员工授权流程已执行并清理。
@@ -0,0 +1,18 @@
# GPU 健康、独占资源与终端日志归档
## 已实现
- `GET /api/v1/resources/gpus?node=...` 在每个 GPU 上返回 `health`:PVE PCI 清单中的驱动字段、驱动绑定状态、来源和采集时间。标准 PVE PCI 接口没有温度、利用率、显存指标时返回 `status=not_collected` 和空值,不把 VM 配置引用当作健康数据。
- `GET /api/v1/resources/gpu-exclusive?node=...` 提供独占组分配前检查:advisory lock、成员完整性、PVE 实际电源、活跃终端会话、未完成任务、冲突原因、`allocationReady` 和 `releaseReady`。接口只读,不会关机、切换显卡或重放任务。
- 终端成功回传 `collect-logs` 后,日志写入 `terminal_log_archives`,按终端和时间建立索引,保留 30 天,并通过 `GET /api/v1/terminal-logs` 支持终端、时间范围和关键词检索。沿用诊断脱敏;这是回传快照归档,不是实时日志流。
- 高级版 GPU 页面显示健康状态、驱动绑定、独占资源冲突和释放条件;日志与诊断页面增加持续归档检索区。
## 真实边界
温度、利用率和显存需要节点采集代理或厂商工具接入,当前 API 明确显示“未采集”。独占资源的释放状态只代表平台检查条件满足,实际 GPU 切换仍走既有终端准备任务和核对流程。
## 验证
- `docker run --rm -v "${PWD}:/src" -w /src/platform golang:1.27-alpine sh -c 'go test ./...'` 通过。
- `npm run typecheck` 和生产构建通过。
- 真实控制台 `http://127.0.0.1:5182` 已重新部署;root 登录后真实读取独占组和 PVE GPU 清单,健康字段显示 `not_collected`,独占组正确报告 VM 101 正在运行导致不可分配。
+2
View File
@@ -87,3 +87,5 @@
11-11 已部署[通用版与高级版](cloud-editions-20261111.md):通用版五个日常入口,真实员工授权、设备登记、只读问题诊断和简明操作记录;高级版保留专业管理并补齐显卡准备超时任务核对入口。模型暂不接入,界面模式不改变账号权限或原功能验收范围。
11-11 已部署[高级版日志与诊断中心](cloud-diagnostics-20261111.md):按唯一设备/桌面关联任务、会话、告警、审计和设备日志,支持 7 天内筛选、分页与完整 ZIP 导出;真实盒子通过页面采集成功并展示/导出。6 项后端专项、Go 全量/vet、25 项页面回归及真实权限/深浅/窄屏检查通过。持续实时日志、全量归档和 VM 客体日志自动采集仍未实现。
12-11 通用版工作台已加入不依赖模型的固定场景方案向导:匹配员工和已有桌面、展示影响、确认后调用真实授权接口,并在对象不唯一或场景不支持时阻止写操作。前端检查、构建和 15 项通用版浏览器回归通过;已发布到真实 5182,并完成临时员工授权执行与清理。
@@ -0,0 +1,7 @@
# 通用版固定场景助手验收证据
- `browser-verification.json`:真实 5182 浏览器生成方案、确认执行授权、高级版切换、390 宽度及无失败请求。
- `deployment.json`:控制台镜像替换记录,API 和 PostgreSQL 未重启。
- `state-verification.json`:临时员工和授权已清理,现场范围为 console-only。
真实执行使用临时员工和已有桌面,授权成功后按唯一 ID 删除临时用户;未操作 VM 电源、GPU、终端命令或 PVE 配置。
@@ -0,0 +1,15 @@
{
"time": "2026-11-12T07:01:55.803Z",
"realAPI": true,
"temporaryUserCreated": true,
"temporaryUserId": "user_2e9f1c7273f024dd54aec2842a5833cd",
"desktopId": "desk_29cf3c79d9fc1b4a25e1bfe55c5c943f",
"planGeneratedFromRealNames": true,
"authorizationExecutedThroughRealUI": true,
"authorizationObservedInAPI": true,
"advancedSwitchStillWorks": true,
"mobileWidth": 390,
"scriptErrors": [],
"failedAPIResponses": [],
"blockedBusinessWrites": []
}
@@ -0,0 +1,12 @@
{
"deployedAt": "2026-11-12T07:01:29.779904+00:00",
"oldConsoleImage": "sha256:d2c0983ac44701828ab35d769f54ae6451f7ea37844455009bb7346fece3c38c",
"newConsoleImage": "sha256:ffdfb41a3f53647d9c8eb1c495b32d2947ba422933eb1879b85907a59a2b9066",
"apiImageUnchanged": true,
"databaseStartedAt": "2026-11-12T06:45:53.898390299Z",
"databaseRestarted": false,
"oldImageTag": "vdi-cloud-live-console:before-assistant-final-20261112-150122",
"databaseChanged": false,
"scope": "console-only",
"ambiguityGuardAdded": true
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

@@ -0,0 +1,13 @@
{
"time": "2026-11-12T07:14:12.903748+00:00",
"temporaryUserAndBindingsAfterCleanup": {
"users": 0,
"bindings": 0
},
"consoleImage": "sha256:ffdfb41a3f53647d9c8eb1c495b32d2947ba422933eb1879b85907a59a2b9066",
"apiImageUnchanged": true,
"databaseRestarted": false,
"databaseChanged": false,
"temporaryUserAuthorizationExecutedAndRemoved": true,
"scope": "console-only"
}
+572 -1
View File
@@ -6537,6 +6537,128 @@
}
}
},
"/resources/gpu-exclusive": {
"get": {
"operationId": "gpuExclusiveStatus",
"security": [
{
"adminBearer": []
}
],
"parameters": [
{
"name": "node",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Successful response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/GPUExclusiveStatus"
}
}
}
},
"400": {
"description": "Request failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"401": {
"description": "Request failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "Request failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"404": {
"description": "Request failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"409": {
"description": "Request failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"429": {
"description": "Request failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"500": {
"description": "Request failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"502": {
"description": "Request failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"503": {
"description": "Request failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/resources/vms": {
"get": {
"operationId": "discoverVMs",
@@ -11754,6 +11876,168 @@
"description": "Super administrators and operators only. ZIP of all matching events, up to 2000, with diagnostic JSON and scope notes. Uses the same object/time/source filters, ignores pagination, and records the export audit."
}
},
"/terminal-logs": {
"get": {
"operationId": "terminalLogArchives",
"security": [
{
"adminBearer": []
}
],
"parameters": [
{
"name": "terminalId",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "keyword",
"in": "query",
"schema": {
"type": "string",
"maxLength": 200
}
},
{
"name": "from",
"in": "query",
"schema": {
"type": "string",
"format": "date-time"
}
},
{
"name": "to",
"in": "query",
"schema": {
"type": "string",
"format": "date-time"
}
},
{
"name": "page",
"in": "query",
"schema": {
"type": "integer",
"minimum": 1
}
},
{
"name": "pageSize",
"in": "query",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 100
}
}
],
"responses": {
"200": {
"description": "Successful response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TerminalLogArchivePage"
}
}
}
},
"400": {
"description": "Request failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"401": {
"description": "Request failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"403": {
"description": "Request failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"404": {
"description": "Request failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"409": {
"description": "Request failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"429": {
"description": "Request failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"500": {
"description": "Request failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"502": {
"description": "Request failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"503": {
"description": "Request failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/audit-logs": {
"get": {
"operationId": "audits",
@@ -14107,6 +14391,59 @@
],
"additionalProperties": false
},
"GPUHealth": {
"type": "object",
"properties": {
"status": {
"type": "string"
},
"source": {
"type": "string"
},
"observedAt": {
"type": "string",
"format": "date-time"
},
"temperatureC": {
"type": "number",
"nullable": true
},
"utilizationPercent": {
"type": "number",
"nullable": true
},
"memoryUsedBytes": {
"type": "integer",
"nullable": true
},
"memoryTotalBytes": {
"type": "integer",
"nullable": true
},
"driver": {
"type": "string"
},
"driverBinding": {
"type": "string",
"enum": [
"bound",
"unknown"
]
}
},
"required": [
"status",
"source",
"observedAt",
"temperatureC",
"utilizationPercent",
"memoryUsedBytes",
"memoryTotalBytes",
"driver",
"driverBinding"
],
"additionalProperties": false
},
"GPUItem": {
"type": "object",
"properties": {
@@ -14144,6 +14481,9 @@
"multiple"
]
},
"health": {
"$ref": "#/components/schemas/GPUHealth"
},
"references": {
"type": "array",
"items": {
@@ -14161,6 +14501,7 @@
"class",
"iommuGroup",
"state",
"health",
"references"
],
"additionalProperties": false
@@ -14215,6 +14556,38 @@
"items": {
"$ref": "#/components/schemas/GPUIssue"
}
},
"health": {
"type": "object",
"properties": {
"status": {
"type": "string"
},
"source": {
"type": "string"
},
"temperature": {
"type": "string"
},
"utilization": {
"type": "string"
},
"memory": {
"type": "string"
},
"driverBinding": {
"type": "string"
}
},
"required": [
"status",
"source",
"temperature",
"utilization",
"memory",
"driverBinding"
],
"additionalProperties": false
}
},
"required": [
@@ -14224,7 +14597,8 @@
"visibleVmCount",
"scannedVmCount",
"items",
"issues"
"issues",
"health"
],
"additionalProperties": false
},
@@ -17171,6 +17545,203 @@
],
"additionalProperties": false
},
"TerminalLogArchive": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"taskId": {
"type": "string"
},
"terminalId": {
"type": "string"
},
"terminalName": {
"type": "string"
},
"capturedAt": {
"type": "string",
"format": "date-time"
},
"bootId": {
"type": "string"
},
"agentVersion": {
"type": "string"
},
"kioskState": {
"type": "string"
},
"logText": {
"type": "string"
}
},
"required": [
"id",
"taskId",
"terminalId",
"terminalName",
"capturedAt",
"bootId",
"agentVersion",
"kioskState",
"logText"
],
"additionalProperties": false
},
"TerminalLogArchivePage": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/components/schemas/TerminalLogArchive"
}
},
"page": {
"type": "integer"
},
"pageSize": {
"type": "integer"
},
"total": {
"type": "integer"
},
"observedAt": {
"type": "string",
"format": "date-time"
},
"retentionDays": {
"type": "integer"
}
},
"required": [
"items",
"page",
"pageSize",
"total",
"observedAt",
"retentionDays"
],
"additionalProperties": false
},
"GPUExclusiveMember": {
"type": "object",
"properties": {
"desktopId": {
"type": "string"
},
"name": {
"type": "string"
},
"vmid": {
"type": "integer"
},
"powerState": {
"type": "string"
},
"powerObserved": {
"type": "boolean"
},
"activeSessions": {
"type": "integer"
},
"activeTasks": {
"type": "integer"
},
"conflicts": {
"type": "array",
"items": {
"type": "string"
}
},
"eligible": {
"type": "boolean"
}
},
"required": [
"desktopId",
"name",
"vmid",
"powerState",
"powerObserved",
"activeSessions",
"activeTasks",
"conflicts",
"eligible"
],
"additionalProperties": false
},
"GPUExclusiveStatus": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"node": {
"type": "string"
},
"vmids": {
"type": "array",
"items": {
"type": "integer"
}
},
"observedAt": {
"type": "string",
"format": "date-time"
},
"lockAvailable": {
"type": "boolean"
},
"groupComplete": {
"type": "boolean"
},
"members": {
"type": "array",
"items": {
"$ref": "#/components/schemas/GPUExclusiveMember"
}
},
"conflicts": {
"type": "array",
"items": {
"type": "string"
}
},
"allocationReady": {
"type": "boolean"
},
"releaseReady": {
"type": "boolean"
},
"releasePolicy": {
"type": "string"
},
"activeTasks": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Task"
}
}
},
"required": [
"enabled",
"node",
"vmids",
"observedAt",
"lockAvailable",
"groupComplete",
"members",
"conflicts",
"allocationReady",
"releaseReady",
"releasePolicy",
"activeTasks"
],
"additionalProperties": false
},
"DesktopList": {
"type": "object",
"properties": {
+2
View File
@@ -78,3 +78,5 @@ CSV 导入、90 天会话历史及节点历史曲线/告警规则已补齐,见
11-11 已部署[管理员双重验证](cloud-mfa-20261111.md):本人可选绑定身份验证器,动态码与一次性恢复码登录,支持更新恢复码和关闭。既有账号默认不启用。
11-11 高级版[日志与诊断中心](cloud-diagnostics-20261111.md)已部署并完成真实盒子采集、关联查询和诊断包下载验收,不再作为未实现项重复排期。后续日志方向为持续推送、服务端归档及 VM 客体采集;定时任务、备份恢复、OTA 等高级事项仍按各自计划与验收条件推进,本轮未实现这些功能。
12-11 已部署[通用版固定场景助手向导](cloud-assistant-wizard-20261112.md):当前不接模型,先把“给员工分配已有桌面”等请求转换为可核对方案并复用真实授权接口。批量电源预检、终端无桌面诊断和模型接入仍待后续逐项验收。