增加服务器管理增删改差

This commit is contained in:
dengqichen 2025-10-30 14:17:16 +08:00
parent b7740e43bb
commit 2165427ec1
21 changed files with 800 additions and 17 deletions

View File

@ -0,0 +1,49 @@
package com.qqchen.deploy.backend.deploy.api;
import com.qqchen.deploy.backend.deploy.dto.ServerDTO;
import com.qqchen.deploy.backend.deploy.dto.ServerInitializeDTO;
import com.qqchen.deploy.backend.deploy.entity.Server;
import com.qqchen.deploy.backend.deploy.query.ServerQuery;
import com.qqchen.deploy.backend.deploy.service.IServerService;
import com.qqchen.deploy.backend.framework.api.Response;
import com.qqchen.deploy.backend.framework.controller.BaseController;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 服务器管理 Controller
*/
@Slf4j
@RestController
@RequestMapping("/api/v1/server")
@Tag(name = "服务器管理", description = "服务器管理相关接口")
public class ServerApiController
extends BaseController<Server, ServerDTO, Long, ServerQuery> {
@Resource
private IServerService serverService;
@Operation(summary = "初始化服务器硬件信息", description = "前端SSH采集服务器硬件信息后回调此接口")
@PostMapping("/{id}/initialize")
public Response<ServerDTO> initializeServerInfo(
@Parameter(description = "服务器ID", required = true) @PathVariable Long id,
@Parameter(description = "服务器硬件信息", required = true) @Valid @RequestBody ServerInitializeDTO dto
) {
ServerDTO result = serverService.initializeServerInfo(id, dto);
return Response.success(result);
}
@Override
protected void exportData(HttpServletResponse response, List<ServerDTO> data) {
log.info("导出服务器数据,数据量:{}", data.size());
}
}

View File

@ -0,0 +1,35 @@
package com.qqchen.deploy.backend.deploy.api;
import com.qqchen.deploy.backend.deploy.dto.ServerCategoryDTO;
import com.qqchen.deploy.backend.deploy.entity.ServerCategory;
import com.qqchen.deploy.backend.deploy.query.ServerCategoryQuery;
import com.qqchen.deploy.backend.deploy.service.IServerCategoryService;
import com.qqchen.deploy.backend.framework.controller.BaseController;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 服务器分类 Controller
*/
@Slf4j
@RestController
@RequestMapping("/api/v1/server-category")
@Tag(name = "服务器分类管理", description = "服务器分类管理相关接口")
public class ServerCategoryApiController
extends BaseController<ServerCategory, ServerCategoryDTO, Long, ServerCategoryQuery> {
@Resource
private IServerCategoryService serverCategoryService;
@Override
protected void exportData(HttpServletResponse response, List<ServerCategoryDTO> data) {
log.info("导出服务器分类数据,数据量:{}", data.size());
}
}

View File

@ -0,0 +1,14 @@
package com.qqchen.deploy.backend.deploy.converter;
import com.qqchen.deploy.backend.deploy.dto.ServerCategoryDTO;
import com.qqchen.deploy.backend.deploy.entity.ServerCategory;
import com.qqchen.deploy.backend.framework.converter.BaseConverter;
import org.mapstruct.Mapper;
/**
* 服务器分类转换器
*/
@Mapper(config = BaseConverter.class)
public interface ServerCategoryConverter extends BaseConverter<ServerCategory, ServerCategoryDTO> {
}

View File

@ -0,0 +1,14 @@
package com.qqchen.deploy.backend.deploy.converter;
import com.qqchen.deploy.backend.deploy.dto.ServerDTO;
import com.qqchen.deploy.backend.deploy.entity.Server;
import com.qqchen.deploy.backend.framework.converter.BaseConverter;
import org.mapstruct.Mapper;
/**
* 服务器转换器
*/
@Mapper(config = BaseConverter.class)
public interface ServerConverter extends BaseConverter<Server, ServerDTO> {
}

View File

@ -0,0 +1,44 @@
package com.qqchen.deploy.backend.deploy.dto;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 服务器分类 DTO
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ServerCategoryDTO extends BaseDTO {
/**
* 分类名称
*/
private String name;
/**
* 分类编码
*/
private String code;
/**
* 图标
*/
private String icon;
/**
* 分类描述
*/
private String description;
/**
* 排序
*/
private Integer sort;
/**
* 是否启用
*/
private Boolean enabled;
}

View File

@ -0,0 +1,92 @@
package com.qqchen.deploy.backend.deploy.dto;
import com.qqchen.deploy.backend.deploy.enums.OsTypeEnum;
import com.qqchen.deploy.backend.deploy.enums.ServerStatusEnum;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 服务器 DTO
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ServerDTO extends BaseDTO {
/**
* 服务器名称
*/
private String serverName;
/**
* IP地址
*/
private String hostIp;
/**
* SSH端口
*/
private Integer sshPort;
/**
* SSH用户名
*/
private String sshUser;
/**
* 服务器分类ID
*/
private Long categoryId;
/**
* 操作系统类型
*/
private OsTypeEnum osType;
/**
* 操作系统版本
*/
private String osVersion;
/**
* 主机名
*/
private String hostname;
/**
* 连接状态
*/
private ServerStatusEnum status;
/**
* 服务器描述
*/
private String description;
/**
* CPU核心数
*/
private Integer cpuCores;
/**
* 内存大小(GB)
*/
private Integer memorySize;
/**
* 磁盘大小(GB)
*/
private Integer diskSize;
/**
* 标签JSON格式
*/
private String tags;
/**
* 最后连接时间
*/
private LocalDateTime lastConnectTime;
}

View File

@ -0,0 +1,48 @@
package com.qqchen.deploy.backend.deploy.dto;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* 服务器初始化 DTO
* 用于前端SSH采集硬件信息后回调
*/
@Data
public class ServerInitializeDTO {
/**
* CPU核心数
*/
@NotNull(message = "CPU核心数不能为空")
@Min(value = 1, message = "CPU核心数至少为1")
private Integer cpuCores;
/**
* 内存大小(GB)
*/
@NotNull(message = "内存大小不能为空")
@Min(value = 1, message = "内存大小至少为1GB")
private Integer memorySize;
/**
* 磁盘大小(GB)
*/
@NotNull(message = "磁盘大小不能为空")
@Min(value = 1, message = "磁盘大小至少为1GB")
private Integer diskSize;
/**
* 操作系统版本
*/
@NotBlank(message = "操作系统版本不能为空")
private String osVersion;
/**
* 主机名
*/
@NotBlank(message = "主机名不能为空")
private String hostname;
}

View File

@ -0,0 +1,112 @@
package com.qqchen.deploy.backend.deploy.entity;
import com.qqchen.deploy.backend.deploy.enums.OsTypeEnum;
import com.qqchen.deploy.backend.deploy.enums.ServerStatusEnum;
import com.qqchen.deploy.backend.framework.domain.Entity;
import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 服务器实体
*/
@Data
@EqualsAndHashCode(callSuper = true)
@jakarta.persistence.Entity
@Table(name = "deploy_server")
public class Server extends Entity<Long> {
/**
* 服务器名称
*/
@Column(name = "server_name", nullable = false, length = 100)
private String serverName;
/**
* IP地址
*/
@Column(name = "host_ip", nullable = false, unique = true, length = 50)
private String hostIp;
/**
* SSH端口
*/
@Column(name = "ssh_port")
private Integer sshPort;
/**
* SSH用户名
*/
@Column(name = "ssh_user", length = 50)
private String sshUser;
/**
* 服务器分类ID
*/
@Column(name = "category_id")
private Long categoryId;
/**
* 操作系统类型
*/
@Enumerated(EnumType.STRING)
@Column(name = "os_type", length = 20)
private OsTypeEnum osType;
/**
* 操作系统版本
*/
@Column(name = "os_version", length = 100)
private String osVersion;
/**
* 主机名
*/
@Column(name = "hostname", length = 100)
private String hostname;
/**
* 连接状态
*/
@Enumerated(EnumType.STRING)
@Column(name = "status", length = 20)
private ServerStatusEnum status;
/**
* 服务器描述
*/
@Column(name = "description", length = 500)
private String description;
/**
* CPU核心数
*/
@Column(name = "cpu_cores")
private Integer cpuCores;
/**
* 内存大小(GB)
*/
@Column(name = "memory_size")
private Integer memorySize;
/**
* 磁盘大小(GB)
*/
@Column(name = "disk_size")
private Integer diskSize;
/**
* 标签JSON格式
*/
@Column(name = "tags", columnDefinition = "JSON")
private String tags;
/**
* 最后连接时间
*/
@Column(name = "last_connect_time")
private LocalDateTime lastConnectTime;
}

View File

@ -0,0 +1,53 @@
package com.qqchen.deploy.backend.deploy.entity;
import com.qqchen.deploy.backend.framework.domain.Entity;
import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 服务器分类实体
*/
@Data
@EqualsAndHashCode(callSuper = true)
@jakarta.persistence.Entity
@Table(name = "deploy_server_category")
public class ServerCategory extends Entity<Long> {
/**
* 分类名称
*/
@Column(name = "name", nullable = false, unique = true, length = 50)
private String name;
/**
* 分类编码
*/
@Column(name = "code", nullable = false, unique = true, length = 50)
private String code;
/**
* 图标
*/
@Column(name = "icon", length = 50)
private String icon;
/**
* 分类描述
*/
@Column(name = "description", length = 500)
private String description;
/**
* 排序
*/
@Column(name = "sort")
private Integer sort;
/**
* 是否启用
*/
@Column(name = "enabled")
private Boolean enabled;
}

View File

@ -0,0 +1,22 @@
package com.qqchen.deploy.backend.deploy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 操作系统类型枚举
*/
@Getter
@AllArgsConstructor
public enum OsTypeEnum {
LINUX("Linux", "Linux系列操作系统"),
WINDOWS("Windows", "Windows系列操作系统"),
MACOS("MacOS", "MacOS系列操作系统"),
UNIX("Unix", "Unix系列操作系统"),
OTHER("Other", "其他操作系统");
private final String code;
private final String description;
}

View File

@ -0,0 +1,19 @@
package com.qqchen.deploy.backend.deploy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 服务器连接状态枚举
*/
@Getter
@AllArgsConstructor
public enum ServerStatusEnum {
ONLINE("在线", "服务器在线可用"),
OFFLINE("离线", "服务器离线或不可达"),
UNKNOWN("未知", "服务器状态未知");
private final String name;
private final String description;
}

View File

@ -0,0 +1,34 @@
package com.qqchen.deploy.backend.deploy.query;
import com.qqchen.deploy.backend.framework.annotation.QueryField;
import com.qqchen.deploy.backend.framework.enums.QueryType;
import com.qqchen.deploy.backend.framework.query.BaseQuery;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 服务器分类查询对象
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ServerCategoryQuery extends BaseQuery {
/**
* 分类名称模糊查询
*/
@QueryField(field = "name", type = QueryType.LIKE)
private String name;
/**
* 分类编码模糊查询
*/
@QueryField(field = "code", type = QueryType.LIKE)
private String code;
/**
* 是否启用
*/
@QueryField(field = "enabled")
private Boolean enabled;
}

View File

@ -0,0 +1,48 @@
package com.qqchen.deploy.backend.deploy.query;
import com.qqchen.deploy.backend.deploy.enums.OsTypeEnum;
import com.qqchen.deploy.backend.deploy.enums.ServerStatusEnum;
import com.qqchen.deploy.backend.framework.annotation.QueryField;
import com.qqchen.deploy.backend.framework.enums.QueryType;
import com.qqchen.deploy.backend.framework.query.BaseQuery;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 服务器查询对象
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ServerQuery extends BaseQuery {
/**
* 服务器名称模糊查询
*/
@QueryField(field = "serverName", type = QueryType.LIKE)
private String serverName;
/**
* IP地址模糊查询
*/
@QueryField(field = "hostIp", type = QueryType.LIKE)
private String hostIp;
/**
* 服务器分类ID
*/
@QueryField(field = "categoryId")
private Long categoryId;
/**
* 操作系统类型
*/
@QueryField(field = "osType")
private OsTypeEnum osType;
/**
* 连接状态
*/
@QueryField(field = "status")
private ServerStatusEnum status;
}

View File

@ -0,0 +1,37 @@
package com.qqchen.deploy.backend.deploy.repository;
import com.qqchen.deploy.backend.deploy.entity.ServerCategory;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import org.springframework.stereotype.Repository;
/**
* 服务器分类仓储接口
*/
@Repository
public interface IServerCategoryRepository extends IBaseRepository<ServerCategory, Long> {
/**
* 根据分类编码查询排除已删除
*
* @param code 分类编码
* @return 服务器分类
*/
ServerCategory findByCodeAndDeletedFalse(String code);
/**
* 检查分类名称是否存在排除已删除
*
* @param name 分类名称
* @return 是否存在
*/
boolean existsByNameAndDeletedFalse(String name);
/**
* 检查分类编码是否存在排除已删除
*
* @param code 分类编码
* @return 是否存在
*/
boolean existsByCodeAndDeletedFalse(String code);
}

View File

@ -0,0 +1,29 @@
package com.qqchen.deploy.backend.deploy.repository;
import com.qqchen.deploy.backend.deploy.entity.Server;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import org.springframework.stereotype.Repository;
/**
* 服务器仓储接口
*/
@Repository
public interface IServerRepository extends IBaseRepository<Server, Long> {
/**
* 根据IP地址查询排除已删除
*
* @param hostIp IP地址
* @return 服务器
*/
Server findByHostIpAndDeletedFalse(String hostIp);
/**
* 检查IP地址是否存在排除已删除
*
* @param hostIp IP地址
* @return 是否存在
*/
boolean existsByHostIpAndDeletedFalse(String hostIp);
}

View File

@ -0,0 +1,13 @@
package com.qqchen.deploy.backend.deploy.service;
import com.qqchen.deploy.backend.deploy.dto.ServerCategoryDTO;
import com.qqchen.deploy.backend.deploy.entity.ServerCategory;
import com.qqchen.deploy.backend.deploy.query.ServerCategoryQuery;
import com.qqchen.deploy.backend.framework.service.IBaseService;
/**
* 服务器分类服务接口
*/
public interface IServerCategoryService extends IBaseService<ServerCategory, ServerCategoryDTO, ServerCategoryQuery, Long> {
}

View File

@ -0,0 +1,23 @@
package com.qqchen.deploy.backend.deploy.service;
import com.qqchen.deploy.backend.deploy.dto.ServerDTO;
import com.qqchen.deploy.backend.deploy.dto.ServerInitializeDTO;
import com.qqchen.deploy.backend.deploy.entity.Server;
import com.qqchen.deploy.backend.deploy.query.ServerQuery;
import com.qqchen.deploy.backend.framework.service.IBaseService;
/**
* 服务器服务接口
*/
public interface IServerService extends IBaseService<Server, ServerDTO, ServerQuery, Long> {
/**
* 初始化服务器硬件信息由前端SSH采集后回调
*
* @param serverId 服务器ID
* @param dto 服务器硬件信息
* @return 更新后的服务器信息
*/
ServerDTO initializeServerInfo(Long serverId, ServerInitializeDTO dto);
}

View File

@ -0,0 +1,29 @@
package com.qqchen.deploy.backend.deploy.service.impl;
import com.qqchen.deploy.backend.deploy.dto.ServerCategoryDTO;
import com.qqchen.deploy.backend.deploy.entity.ServerCategory;
import com.qqchen.deploy.backend.deploy.query.ServerCategoryQuery;
import com.qqchen.deploy.backend.deploy.repository.IServerCategoryRepository;
import com.qqchen.deploy.backend.deploy.service.IServerCategoryService;
import com.qqchen.deploy.backend.framework.annotation.ServiceType;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 服务器分类服务实现
*/
@Slf4j
@Service
@ServiceType(ServiceType.Type.DATABASE)
public class ServerCategoryServiceImpl
extends BaseServiceImpl<ServerCategory, ServerCategoryDTO, ServerCategoryQuery, Long>
implements IServerCategoryService {
private final IServerCategoryRepository serverCategoryRepository;
public ServerCategoryServiceImpl(IServerCategoryRepository serverCategoryRepository) {
this.serverCategoryRepository = serverCategoryRepository;
}
}

View File

@ -0,0 +1,61 @@
package com.qqchen.deploy.backend.deploy.service.impl;
import com.qqchen.deploy.backend.deploy.dto.ServerDTO;
import com.qqchen.deploy.backend.deploy.dto.ServerInitializeDTO;
import com.qqchen.deploy.backend.deploy.entity.Server;
import com.qqchen.deploy.backend.deploy.enums.ServerStatusEnum;
import com.qqchen.deploy.backend.deploy.query.ServerQuery;
import com.qqchen.deploy.backend.deploy.repository.IServerRepository;
import com.qqchen.deploy.backend.deploy.service.IServerService;
import com.qqchen.deploy.backend.framework.annotation.ServiceType;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
/**
* 服务器服务实现
*/
@Slf4j
@Service
@ServiceType(ServiceType.Type.DATABASE)
public class ServerServiceImpl
extends BaseServiceImpl<Server, ServerDTO, ServerQuery, Long>
implements IServerService {
private final IServerRepository serverRepository;
public ServerServiceImpl(IServerRepository serverRepository) {
this.serverRepository = serverRepository;
}
@Override
@Transactional
public ServerDTO initializeServerInfo(Long serverId, ServerInitializeDTO dto) {
// 1. 查询服务器
Server server = serverRepository.findById(serverId)
.orElseThrow(() -> new BusinessException(ResponseCode.DATA_NOT_FOUND));
// 2. 更新硬件信息
server.setCpuCores(dto.getCpuCores());
server.setMemorySize(dto.getMemorySize());
server.setDiskSize(dto.getDiskSize());
server.setOsVersion(dto.getOsVersion());
server.setHostname(dto.getHostname());
server.setStatus(ServerStatusEnum.ONLINE);
server.setLastConnectTime(LocalDateTime.now());
// 3. 保存并返回
Server updated = serverRepository.save(server);
log.info("服务器初始化成功: serverId={}, cpuCores={}, memorySize={}GB, diskSize={}GB",
serverId, dto.getCpuCores(), dto.getMemorySize(), dto.getDiskSize());
return converter.toDto(updated);
}
}

View File

@ -67,6 +67,8 @@ VALUES
(202, '应用管理', '/deploy/applications', '/src/pages/Deploy/Application/List/index', 'AppstoreOutlined', 2, 200, 2, FALSE, TRUE, 'system', '2024-01-01 00:00:00', 0, FALSE),
-- 定时任务管理
(203, '定时任务管理', '/deploy/schedule-jobs', '/src/pages/Deploy/ScheduleJob/List/index', 'ClockCircleOutlined', 2, 200, 3, FALSE, TRUE, 'system', '2024-01-01 00:00:00', 0, FALSE),
-- 服务器管理
(204, '服务器管理', '/deploy/servers', '/src/pages/Deploy/Server/List/index', 'CloudServerOutlined', 2, 200, 4, FALSE, TRUE, 'system', '2024-01-01 00:00:00', 0, FALSE),
-- 资源管理
(300, '资源管理', '/resource', 'Layout', 'DatabaseOutlined', 1, NULL, 3, FALSE, TRUE, 'system', '2024-01-01 00:00:00', 0, FALSE),

View File

@ -1025,28 +1025,33 @@ CREATE TABLE deploy_server_category (
-- 服务器表
CREATE TABLE deploy_server (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
server_name VARCHAR(100) NOT NULL COMMENT '服务器名称',
host_ip VARCHAR(50) NOT NULL UNIQUE COMMENT 'IP地址',
ssh_port INT DEFAULT 22 COMMENT 'SSH端口',
ssh_user VARCHAR(50) NULL COMMENT 'SSH用户名',
category_id BIGINT NULL COMMENT '服务器分类ID',
os_type VARCHAR(20) NULL COMMENT '操作系统LINUX/WINDOWS/MACOS',
environment VARCHAR(20) NULL COMMENT '环境DEV/TEST/PROD',
status VARCHAR(20) DEFAULT 'OFFLINE' COMMENT '连接状态ONLINE/OFFLINE',
description VARCHAR(500) NULL COMMENT '服务器描述',
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
server_name VARCHAR(100) NOT NULL COMMENT '服务器名称',
host_ip VARCHAR(50) NOT NULL UNIQUE COMMENT 'IP地址',
ssh_port INT DEFAULT 22 COMMENT 'SSH端口',
ssh_user VARCHAR(50) NULL COMMENT 'SSH用户名',
category_id BIGINT NULL COMMENT '服务器分类ID',
os_type VARCHAR(20) NULL COMMENT '操作系统类型LINUX/WINDOWS/MACOS/UNIX/OTHER',
os_version VARCHAR(100) NULL COMMENT '操作系统版本CentOS 7.9',
hostname VARCHAR(100) NULL COMMENT '主机名',
status VARCHAR(20) DEFAULT 'OFFLINE' COMMENT '连接状态ONLINE/OFFLINE',
description VARCHAR(500) NULL COMMENT '服务器描述',
cpu_cores INT NULL COMMENT 'CPU核心数',
memory_size INT NULL COMMENT '内存大小(GB)',
disk_size INT NULL COMMENT '磁盘大小(GB)',
tags JSON NULL COMMENT '标签JSON格式',
last_connect_time DATETIME NULL COMMENT '最后连接时间',
-- 审计字段
create_by VARCHAR(50) NULL COMMENT '创建人',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_by VARCHAR(50) NULL COMMENT '更新人',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
version INT DEFAULT 1 COMMENT '版本号',
deleted BOOLEAN DEFAULT FALSE COMMENT '是否删除',
create_by VARCHAR(50) NULL COMMENT '创建人',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_by VARCHAR(50) NULL COMMENT '更新人',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
version INT DEFAULT 1 COMMENT '版本号',
deleted BOOLEAN DEFAULT FALSE COMMENT '是否删除',
INDEX idx_host_ip (host_ip),
INDEX idx_category_id (category_id),
INDEX idx_environment (environment),
INDEX idx_status (status),
CONSTRAINT fk_server_category FOREIGN KEY (category_id) REFERENCES deploy_server_category(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='服务器管理表';