From ecbf63e6ceb74cea1674c2e906127b36d05c0689 Mon Sep 17 00:00:00 2001 From: yanweidonog Date: Sat, 28 Mar 2026 16:15:08 +0800 Subject: [PATCH] fix --- src/api/ops/server.ts | 27 +- src/views/ops/pages/dc/server/api.md | 365 +++++++++++++++ .../dc/server/components/ServerFormDialog.vue | 52 ++- .../ops/pages/dc/server/config/columns.ts | 66 +-- .../ops/pages/dc/server/config/search-form.ts | 34 +- src/views/ops/pages/dc/server/index.vue | 442 +++++------------- 6 files changed, 573 insertions(+), 413 deletions(-) create mode 100644 src/views/ops/pages/dc/server/api.md diff --git a/src/api/ops/server.ts b/src/api/ops/server.ts index 94868c6..87fdea3 100644 --- a/src/api/ops/server.ts +++ b/src/api/ops/server.ts @@ -1,40 +1,31 @@ import { request } from "@/api/request"; /** 获取服务器列表(分页) */ -export const fetchServerList = (data?: { +export const fetchServerList = (params?: { page?: number; - page_size?: number; + size?: number; keyword?: string; - datacenter_id?: number; - rack_id?: number; - status?: string; - sort?: string; - order?: string; + collect_on?: boolean; }) => { - return request.post("/Assets/v1/server/list", data || {}); + return request.get("/DC-Control/v1/servers", { params }); }; /** 获取服务器详情 */ export const fetchServerDetail = (id: number) => { - return request.get(`/Assets/v1/server/detail/${id}`); + return request.get(`/DC-Control/v1/servers/${id}`); }; /** 创建服务器 */ export const createServer = (data: any) => { - return request.post("/Assets/v1/server/create", data); + return request.post("/DC-Control/v1/servers", data); }; /** 更新服务器 */ -export const updateServer = (data: any) => { - return request.put("/Assets/v1/server/update", data); +export const updateServer = (id: number, data: any) => { + return request.put(`/DC-Control/v1/servers/${id}`, data); }; /** 删除服务器 */ export const deleteServer = (id: number) => { - return request.delete(`/Assets/v1/server/delete/${id}`); -}; - -/** 获取机柜列表(用于下拉选择) */ -export const fetchRackListForSelect = (datacenterId?: number) => { - return request.get("/Assets/v1/rack/all", { params: { datacenter_id: datacenterId } }); + return request.delete(`/DC-Control/v1/servers/${id}`); }; diff --git a/src/views/ops/pages/dc/server/api.md b/src/views/ops/pages/dc/server/api.md new file mode 100644 index 0000000..e255ee4 --- /dev/null +++ b/src/views/ops/pages/dc/server/api.md @@ -0,0 +1,365 @@ +# 服务器管理接口文档 + +## 基础信息 + +- **文档索引**: [README.md](README.md) +- **服务前缀**: `/DC-Control/v1/servers` +- **认证方式**: JWT认证(所有接口都需要JWT Token) +- **Content-Type**: `application/json` + +--- + +## 1. 获取服务器列表 + +### 接口信息 +- **路径**: `GET /DC-Control/v1/servers` +- **描述**: 分页获取服务器列表,支持关键词搜索、按是否启用采集(`collect_on`)过滤 + +### 请求参数 + +#### 查询参数 (Query Parameters) + +| 参数名 | 类型 | 必填 | 默认值 | 说明 | +|--------|------|------|--------|------| +| page | int | 否 | 1 | 页码(必须为正整数) | +| size | int | 否 | 20 | 每页数量(必须为正整数,最大100) | +| keyword | string | 否 | - | 搜索关键词(按**名称、主机地址**模糊匹配,实现见 `ListServers`) | +| collect_on | bool | 否 | - | 是否启用采集过滤,仅 `true`/`false`;缺省或空串不按该条件过滤,非法值返回 400 | + +### 请求示例 + +```http +GET /DC-Control/v1/servers?page=1&size=20&keyword=生产&collect_on=true +Authorization: Bearer {JWT_TOKEN} +``` + +### 返回参数 + +```json +{ + "code": 200, + "message": "success", + "data": { + "total": 50, + "page": 1, + "page_size": 20, + "data": [ + { + "id": 1, + "created_at": "2024-01-01T10:00:00Z", + "updated_at": "2024-01-01T10:00:00Z", + "deleted_at": null, + "server_identity": "server-001", + "name": "生产服务器-001", + "host": "192.168.1.100", + "ip_address": "192.168.1.100", + "description": "生产环境主服务器", + "os": "Linux", + "os_version": "22.04", + "kernel": "x86", + "server_type": "physical", + "tags": "production,web", + "location": "机房A-机架01", + "remote_access": "ssh://user@host", + "agent_config": "", + "status": "online", + "last_check_time": "2024-01-01T10:00:00Z", + "collect_on": true, + "collect_args": "", + "collect_interval": 60, + "collect_last_result": "" + } + ] + } +} +``` + +### 返回字段说明 + +| 字段名 | 类型 | 说明 | +|--------|------|------| +| total | int64 | 总记录数 | +| page | int | 当前页码 | +| page_size | int | 每页数量 | +| data | array | 服务器列表 | +| data[].id | uint | 服务器ID | +| data[].created_at | string | 创建时间 | +| data[].updated_at | string | 更新时间 | +| data[].deleted_at | string\|null | 删除时间(软删除) | +| data[].server_identity | string | 服务器唯一标识(唯一索引) | +| data[].name | string | 服务器名称 | +| data[].host | string | 服务器地址 | +| data[].ip_address | string | IP地址 | +| data[].description | string | 描述信息 | +| data[].os | string | 操作系统:Windows/Linux/Mac/Other 等 | +| data[].os_version | string | 操作系统版本 | +| data[].kernel | string | 内核类型:x86/arm 等 | +| data[].server_type | string | 服务器类型:physical(物理)/virtual(虚拟)/cloud(云) | +| data[].tags | string | 标签,逗号分隔的字符串(如 `prod,web`),可为空 | +| data[].location | string | 位置/机房信息 | +| data[].remote_access | string | 远程访问信息 | +| data[].agent_config | string | Agent 配置 | +| data[].status | string | 状态:online(在线)/offline(离线)/unknown(未知) | +| data[].last_check_time | string | 最后检查时间 | +| data[].collect_on | bool | 是否启用采集 | +| data[].collect_args | string | 采集参数 | +| data[].collect_interval | int | 采集间隔(秒),默认 60 | +| data[].collect_last_result | string | 采集最后结果 | + +--- + +## 2. 获取服务器详情 + +### 接口信息 +- **路径**: `GET /DC-Control/v1/servers/:id` +- **描述**: 根据ID获取单个服务器的详细信息 + +### 请求参数 + +#### 路径参数 (Path Parameters) + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|--------| +| id | uint | 是 | 服务器ID(必须大于0) | + +### 请求示例 + +```http +GET /DC-Control/v1/servers/1 +Authorization: Bearer {JWT_TOKEN} +``` + +### 返回参数 + +返回单体字段与列表项一致(见上一节 `data[]` 字段说明),无嵌套 `collectors` 字段;采集器与服务器通过采集器表 `server_id` 关联,需通过采集器管理接口查询。 + +--- + +## 3. 创建服务器 + +### 接口信息 +- **路径**: `POST /DC-Control/v1/servers` +- **描述**: 创建新的服务器记录 + +### 请求参数 + +#### 请求体 (Request Body) + +| 字段名 | 类型 | 必填 | 说明 | +|--------|------|------|--------| +| server_identity | string | 否 | 服务器唯一标识;**不传或空串时服务端生成 ULID** | +| name | string | 是 | 服务器名称(最大100字符) | +| host | string | 是 | 服务器地址(最大255字符) | +| ip_address | string | 否 | IP地址(最大50字符) | +| description | string | 否 | 描述信息 | +| os | string | 否 | 操作系统类型 | +| os_version | string | 否 | 操作系统版本 | +| kernel | string | 否 | 内核类型 | +| server_type | string | 否 | 服务器类型:physical/virtual/cloud(最大50字符) | +| tags | string | 否 | 标签,逗号分隔(如 `env,role,tier`),最大约 500 字符,可为空 | +| location | string | 否 | 位置/机房信息(最大200字符) | +| remote_access | string | 否 | 远程访问 | +| agent_config | string | 否 | Agent 配置 | +| status | string | 否 | 状态:online/offline/unknown,默认由库表 default 为 unknown | +| last_check_time | string (RFC3339) | 否 | 最后检查时间 | +| collect_on | bool | 否 | 是否启用采集,默认 true | +| collect_args | string | 否 | 采集参数 | +| collect_interval | int | 否 | 采集间隔(秒),默认 60 | +| collect_last_result | string | 否 | 采集最后结果 | + +### 请求示例 + +```http +POST /DC-Control/v1/servers +Authorization: Bearer {JWT_TOKEN} +Content-Type: application/json + +{ + "server_identity": "server-001", + "name": "生产服务器-001", + "host": "192.168.1.100", + "ip_address": "192.168.1.100", + "description": "生产环境主服务器", + "server_type": "physical", + "tags": "production,web,tier1", + "location": "机房A-机架01", + "remote_access": "ssh://ops@192.168.1.100", + "status": "online", + "collect_on": true, + "collect_interval": 60 +} +``` + +### 返回参数 + +```json +{ + "code": 200, + "message": "success", + "data": { + "id": 1, + "created_at": "2024-01-01T10:00:00Z", + "updated_at": "2024-01-01T10:00:00Z", + "deleted_at": null, + "server_identity": "server-001", + "name": "生产服务器-001", + "host": "192.168.1.100", + "ip_address": "192.168.1.100", + "description": "生产环境主服务器", + "os": "", + "os_version": "", + "kernel": "", + "server_type": "physical", + "tags": "production,web,tier1", + "location": "机房A-机架01", + "remote_access": "ssh://ops@192.168.1.100", + "agent_config": "", + "status": "online", + "last_check_time": "0001-01-01T00:00:00Z", + "collect_on": true, + "collect_args": "", + "collect_interval": 60, + "collect_last_result": "" + } +} +``` + +> 创建接口直接返回内存中的 `server` 对象。采集器与服务器的关联请在**采集器管理**接口中维护(如设置采集器的 `server_id`)。 + +### 错误响应 + +当达到许可证限制时,会返回以下错误: + +```json +{ + "code": 400, + "message": "已达到许可证允许的最大服务器数量限制({max_server}),无法创建更多服务器", + "data": null +} +``` + +--- + +## 4. 更新服务器 + +### 接口信息 +- **路径**: `PUT /DC-Control/v1/servers/:id` +- **描述**: 更新服务器信息 + +### 请求参数 + +#### 路径参数 (Path Parameters) + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|--------| +| id | uint | 是 | 服务器ID(必须大于0) | + +#### 请求体 (Request Body) + +| 字段名 | 类型 | 必填 | 说明 | +|--------|------|------|--------| +| server_identity | string | 否 | 服务器唯一标识 | +| name | string | 否 | 服务器名称 | +| host | string | 否 | 服务器地址 | +| ip_address | string | 否 | IP地址 | +| description | string | 否 | 描述信息 | +| server_type | string | 否 | 服务器类型 | +| tags | string | 否 | 标签,逗号分隔 | +| location | string | 否 | 位置/机房信息 | +| remote_access | string | 否 | 远程访问 | +| agent_config | string | 否 | Agent 配置 | +| status | string | 否 | 状态 | +| last_check_time | string (RFC3339) | 否 | 最后检查时间 | +| collect_on | bool | 否 | 是否启用采集(可显式更新为 false) | +| collect_args | string | 否 | 采集参数 | +| collect_interval | int | 否 | 采集间隔(秒) | +| collect_last_result | string | 否 | 采集最后结果 | +| os / os_version / kernel | string | 否 | 操作系统与内核字段 | + +**注意**: 只传需要更新的字段;未出现的列不修改。更新使用 `map` 写入,可将 `collect_on`、`collect_interval` 等设为 `false`/`0`。 + +### 请求示例 + +```http +PUT /DC-Control/v1/servers/1 +Authorization: Bearer {JWT_TOKEN} +Content-Type: application/json + +{ + "description": "更新后的描述", + "status": "offline", + "collect_on": false +} +``` + +### 返回参数 + +```json +{ + "code": 200, + "message": "success", + "data": { + "message": "updated" + } +} +``` + +--- + +## 5. 删除服务器 + +### 接口信息 +- **路径**: `DELETE /DC-Control/v1/servers/:id` +- **描述**: 删除服务器(软删除) + +### 请求参数 + +#### 路径参数 (Path Parameters) + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|--------| +| id | uint | 是 | 服务器ID(必须大于0) | + +### 请求示例 + +```http +DELETE /DC-Control/v1/servers/1 +Authorization: Bearer {JWT_TOKEN} +``` + +### 返回参数 + +```json +{ + "code": 200, + "message": "success", + "data": { + "message": "deleted" + } +} +``` + +--- + +## 错误码说明 + +| 错误码 | 说明 | +|--------|------| +| 200 | 成功 | +| 400 | 参数验证失败(如ID为0、page/size无效等) | +| 401 | 未授权(JWT Token无效或过期) | +| 404 | 资源不存在 | +| 500 | 服务器内部错误 | + +## 注意事项 + +1. 所有接口都需要在请求头中携带JWT Token:`Authorization: Bearer {JWT_TOKEN}` +2. 服务器ID必须大于0 +3. 分页参数page和size必须为正整数,size最大为100 +4. `server_identity` 在库中必须唯一;若创建时不传则由服务端生成 ULID +5. 服务器类型(`server_type`)支持:physical(物理)、virtual(虚拟)、cloud(云) +6. 服务器状态:online(在线)、offline(离线)、unknown(未知) +7. 删除服务器为软删除,数据不会真正删除 +8. **tags** 为可选,任意逗号分隔字符串(如 `env,app,tier`),长度受模型 `varchar(500)` 限制 +9. **许可证限制**:创建服务器时会检查**服务器总条数**是否达到许可证 `MaxServer` 上限,超出则拒绝创建 +10. 服务器 CRUD **不处理**采集器绑定;关联关系通过**采集器管理**接口(更新采集器 `server_id` 等)维护。列表与详情不内嵌采集器数组。 diff --git a/src/views/ops/pages/dc/server/components/ServerFormDialog.vue b/src/views/ops/pages/dc/server/components/ServerFormDialog.vue index d767cc2..b9807d1 100644 --- a/src/views/ops/pages/dc/server/components/ServerFormDialog.vue +++ b/src/views/ops/pages/dc/server/components/ServerFormDialog.vue @@ -121,6 +121,7 @@ import { Message } from '@arco-design/web-vue' import { v4 as uuidv4 } from 'uuid' import type { FormInstance } from '@arco-design/web-vue' import DatacenterSelector from './DatacenterSelector.vue' +import { createServer, updateServer } from '@/api/ops/server' interface Props { visible: boolean @@ -216,17 +217,52 @@ const handleOk = async () => { try { await formRef.value?.validate() - if (!formData.unique_id) { - formData.unique_id = uuidv4() - } - confirmLoading.value = true - await new Promise(resolve => setTimeout(resolve, 1000)) + // 准备提交数据 + const submitData: any = { + name: formData.name, + host: formData.ip, + ip_address: formData.ip, + description: formData.remark, + os: formData.os, + server_type: formData.server_type, + kernel: formData.kernel, + tags: formData.tags, + remote_access: formData.remote_access, + agent_config: formData.agent_config, + collect_on: formData.data_collection, + collect_interval: formData.collection_interval, + } - Message.success(isEdit.value ? '更新成功' : '创建成功') - emit('success') - handleCancel() + // 编辑模式或唯一标识 + if (isEdit.value && props.record?.id) { + // 更新服务器 + const res: any = await updateServer(props.record.id, submitData) + if (res.code === 200 || res.code === 0) { + Message.success('更新成功') + emit('success') + handleCancel() + } else { + Message.error(res.message || '更新失败') + } + } else { + // 创建服务器 + if (!formData.unique_id) { + submitData.server_identity = uuidv4() + } else { + submitData.server_identity = formData.unique_id + } + + const res: any = await createServer(submitData) + if (res.code === 200 || res.code === 0) { + Message.success('创建成功') + emit('success') + handleCancel() + } else { + Message.error(res.message || '创建失败') + } + } } catch (error) { console.error('验证失败:', error) } finally { diff --git a/src/views/ops/pages/dc/server/config/columns.ts b/src/views/ops/pages/dc/server/config/columns.ts index e2c82e1..4973c3e 100644 --- a/src/views/ops/pages/dc/server/config/columns.ts +++ b/src/views/ops/pages/dc/server/config/columns.ts @@ -6,7 +6,7 @@ export const columns = [ slotName: 'id', }, { - dataIndex: 'service_identity', + dataIndex: 'server_identity', title: '唯一标识', width: 150, }, @@ -15,34 +15,34 @@ export const columns = [ title: '名称', width: 150, }, + { + dataIndex: 'host', + title: '主机地址', + width: 150, + }, + { + dataIndex: 'ip_address', + title: 'IP 地址', + width: 150, + }, + { + dataIndex: 'os', + title: '操作系统', + width: 120, + }, + { + dataIndex: 'os_version', + title: '系统版本', + width: 120, + }, { dataIndex: 'server_type', title: '类型', width: 120, }, - { - dataIndex: 'os', - title: '操作系统', - width: 150, - }, - { - dataIndex: 'kernel', - title: '内核类型', - width: 150, - }, - { - dataIndex: 'location', - title: '位置信息', - width: 150, - }, { dataIndex: 'tags', title: '标签', - width: 120, - }, - { - dataIndex: 'ip', - title: 'IP地址', width: 150, }, { @@ -53,30 +53,12 @@ export const columns = [ }, { dataIndex: 'agent_config', - title: 'Agent配置', - width: 150, + title: 'Agent 配置', + width: 100, slotName: 'agent_config', }, { - dataIndex: 'cpu', - title: 'CPU使用率', - width: 150, - slotName: 'cpu', - }, - { - dataIndex: 'memory', - title: '内存使用率', - width: 150, - slotName: 'memory', - }, - { - dataIndex: 'disk', - title: '硬盘使用率', - width: 150, - slotName: 'disk', - }, - { - dataIndex: 'data_collection', + dataIndex: 'collect_on', title: '数据采集', width: 100, slotName: 'data_collection', diff --git a/src/views/ops/pages/dc/server/config/search-form.ts b/src/views/ops/pages/dc/server/config/search-form.ts index af2affe..d608006 100644 --- a/src/views/ops/pages/dc/server/config/search-form.ts +++ b/src/views/ops/pages/dc/server/config/search-form.ts @@ -5,36 +5,18 @@ export const searchFormConfig: FormItem[] = [ field: 'keyword', label: '关键词', type: 'input', - placeholder: '请输入服务器名称、编码或IP', - span: 6, + placeholder: '请输入服务器名称、主机地址', + span: 8, }, { - field: 'datacenter_id', - label: '数据中心', + field: 'collect_on', + label: '数据采集', type: 'select', - placeholder: '请选择数据中心', - options: [], // 需要动态加载 - span: 6, - }, - { - field: 'rack_id', - label: '机柜', - type: 'select', - placeholder: '请选择机柜', - options: [], // 需要动态加载 - span: 6, - }, - { - field: 'status', - label: '状态', - type: 'select', - placeholder: '请选择状态', + placeholder: '请选择采集状态', options: [ - { label: '在线', value: 'online' }, - { label: '离线', value: 'offline' }, - { label: '维护中', value: 'maintenance' }, - { label: '已退役', value: 'retired' }, + { label: '启用', value: true }, + { label: '禁用', value: false }, ], - span: 6, + span: 8, }, ] diff --git a/src/views/ops/pages/dc/server/index.vue b/src/views/ops/pages/dc/server/index.vue index 5afdd03..09926ce 100644 --- a/src/views/ops/pages/dc/server/index.vue +++ b/src/views/ops/pages/dc/server/index.vue @@ -37,13 +37,13 @@ - + - +