feat: 添加AMT批量管理凭证功能

- 功能:AMT凭证的增删改查管理
- 支持设置默认凭证
- 支持单个和批量测试凭证连接
- 数据库表:amt_credential
- 后端:
  * AmtCredential实体类
  * AmtCredentialMapper
  * AmtCredentialService业务逻辑
  * AmtCredentialController控制器
  * 使用Result统一返回格式
- 前端:
  * AMT设置页面重构
  * 凭证列表展示(分页、搜索)
  * 添加/编辑凭证弹窗
  * 批量测试功能
  * API接口封装
- 部署脚本:
  * init_amt_credential_table.bat - 初始化数据库
  * test_amt_api.bat - 测试API接口
  * rebuild_and_start_backend.bat - 重新编译后端
- 文档:
  * AMT_CREDENTIAL_FEATURE.md - 功能说明
  * DEPLOY_AMT_FEATURE.md - 部署说明
This commit is contained in:
lvfengfree 2026-03-01 10:55:11 +08:00
parent 44cb688072
commit fd124d1017
15 changed files with 1258 additions and 19 deletions

171
AMT_CREDENTIAL_FEATURE.md Normal file
View File

@ -0,0 +1,171 @@
# AMT 批量管理凭证功能
## 功能概述
在"系统设置 > AMT设置"页面添加了 AMT 批量管理凭证功能,用于管理 Intel AMT 设备的登录凭证。
## 主要功能
### 1. 凭证管理
- ✅ 添加凭证:创建新的 AMT 登录凭证
- ✅ 编辑凭证:修改现有凭证信息
- ✅ 删除凭证:删除不需要的凭证(默认凭证不可删除)
- ✅ 设为默认:将凭证设置为默认凭证(优先用于新设备)
### 2. 凭证测试
- ✅ 单个测试:测试单个凭证的连接状态
- ✅ 批量测试:批量测试多个凭证的连接状态
### 3. 凭证信息
- 凭证名称:便于识别的名称
- 用户名AMT 登录用户名
- 密码AMT 登录密码(加密存储)
- 描述:凭证的详细说明
- 默认标记:是否为默认凭证
- 状态:启用/禁用
## 技术实现
### 后端
#### 数据库表
- `amt_credential` - AMT 凭证管理表
- 字段credential_id, credential_name, username, password, description, is_default, status, created_by, created_time, updated_time
#### Java 类
- `AmtCredential.java` - 实体类
- `AmtCredentialMapper.java` - MyBatis Mapper
- `AmtCredentialDTO.java` - 数据传输对象
- `AmtCredentialService.java` - 业务逻辑层
- `AmtCredentialController.java` - 控制器层
#### API 接口
- `GET /api/amt/credential/list` - 分页查询凭证列表
- `GET /api/amt/credential/all` - 获取所有启用的凭证
- `GET /api/amt/credential/{id}` - 根据ID获取凭证
- `POST /api/amt/credential` - 创建凭证
- `PUT /api/amt/credential/{id}` - 更新凭证
- `DELETE /api/amt/credential/{id}` - 删除凭证
- `PUT /api/amt/credential/{id}/default` - 设置默认凭证
- `POST /api/amt/credential/{id}/test` - 测试凭证连接
- `POST /api/amt/credential/batch-test` - 批量测试凭证
### 前端
#### 文件
- `src/views/system/amt/index.vue` - AMT 设置页面
- `src/service/api/amt.ts` - AMT API 接口
#### 功能特性
- 数据表格展示凭证列表
- 分页、搜索、筛选
- 添加/编辑凭证弹窗
- 批量选择和批量测试
- 实时状态显示
## 安装步骤
### 1. 初始化数据库表
运行批处理脚本:
```bash
init_amt_credential_table.bat
```
或手动执行 SQL
```bash
mysql -uroot -proot -hlocalhost -P3306 < backend/src/main/resources/sql/create_amt_credential_table.sql
```
### 2. 重启后端服务
```bash
cd backend
mvn spring-boot:run
```
或使用批处理脚本:
```bash
restart_backend_and_test.bat
```
### 3. 访问页面
登录系统后,访问:系统设置 > AMT设置
## 使用说明
### 添加凭证
1. 点击"添加凭证"按钮
2. 填写凭证信息:
- 凭证名称(必填)
- 用户名(必填)
- 密码(必填)
- 描述(可选)
- 是否设为默认
- 状态(启用/禁用)
3. 点击"确定"保存
### 测试凭证
1. 单个测试:点击凭证行的"测试"按钮
2. 批量测试:
- 勾选要测试的凭证
- 点击"批量测试"按钮
- 查看测试结果
### 设置默认凭证
1. 点击凭证行的"设为默认"按钮
2. 系统会自动取消其他凭证的默认状态
3. 默认凭证将优先用于新设备
### 编辑凭证
1. 点击凭证行的"编辑"按钮
2. 修改凭证信息
3. 点击"确定"保存
### 删除凭证
1. 点击凭证行的"删除"按钮
2. 确认删除操作
3. 注意:默认凭证不能删除
## 注意事项
1. **密码安全**:密码在数据库中应该加密存储(当前为明文,建议后续加密)
2. **默认凭证**:系统只能有一个默认凭证
3. **凭证测试**:当前为模拟测试,实际使用时需要集成 AMT SDK
4. **权限控制**:建议只有管理员角色可以访问此功能
## 后续优化建议
1. **密码加密**:使用 AES 或 RSA 加密存储密码
2. **AMT SDK 集成**:集成真实的 AMT SDK 进行连接测试
3. **凭证应用**:将凭证应用到具体设备
4. **操作日志**:记录凭证的创建、修改、删除操作
5. **批量导入**:支持从 CSV/Excel 批量导入凭证
6. **凭证分组**:支持凭证分组管理
## 文件清单
### 后端文件
- `backend/src/main/resources/sql/create_amt_credential_table.sql`
- `backend/src/main/java/com/soybean/admin/entity/AmtCredential.java`
- `backend/src/main/java/com/soybean/admin/mapper/AmtCredentialMapper.java`
- `backend/src/main/java/com/soybean/admin/dto/AmtCredentialDTO.java`
- `backend/src/main/java/com/soybean/admin/service/AmtCredentialService.java`
- `backend/src/main/java/com/soybean/admin/controller/AmtCredentialController.java`
### 前端文件
- `src/views/system/amt/index.vue`
- `src/service/api/amt.ts`
- `src/service/api/index.ts` (更新)
### 脚本文件
- `init_amt_credential_table.bat`
## 测试建议
1. 测试添加凭证功能
2. 测试编辑凭证功能
3. 测试删除凭证功能
4. 测试设置默认凭证功能
5. 测试单个凭证连接
6. 测试批量凭证连接
7. 测试分页和搜索功能
8. 测试表单验证

83
DEPLOY_AMT_FEATURE.md Normal file
View File

@ -0,0 +1,83 @@
# AMT 凭证功能部署说明
## 问题修复
修复了 404 错误,主要问题:
1. ✅ 控制器返回格式不统一 - 已改用 `Result`
2. ✅ 返回代码不匹配 - 已改为 `"0000"`
3. ✅ API 路径前缀问题 - 已移除 `/api` 前缀
## 部署步骤
### 1. 初始化数据库表
```bash
init_amt_credential_table.bat
```
### 2. 重启后端服务
停止当前后端服务,然后重新启动:
```bash
cd backend
mvn spring-boot:run
```
或使用批处理脚本:
```bash
start_backend.bat
```
### 3. 清除浏览器缓存
`Ctrl + Shift + Delete` 清除浏览器缓存
### 4. 访问页面
登录系统后,访问:**系统设置 > AMT设置**
## API 端点
所有 API 端点(无需 `/api` 前缀):
- `GET /amt/credential/list` - 分页查询凭证列表
- `GET /amt/credential/all` - 获取所有启用的凭证
- `GET /amt/credential/{id}` - 根据ID获取凭证
- `POST /amt/credential` - 创建凭证
- `PUT /amt/credential/{id}` - 更新凭证
- `DELETE /amt/credential/{id}` - 删除凭证
- `PUT /amt/credential/{id}/default` - 设置默认凭证
- `POST /amt/credential/{id}/test` - 测试凭证连接
- `POST /amt/credential/batch-test` - 批量测试凭证
## 测试 API
运行测试脚本:
```bash
test_amt_api.bat
```
或手动测试:
```bash
curl -X GET "http://localhost:8080/amt/credential/list?page=1&size=10"
```
## 常见问题
### Q: 仍然显示 404 错误
A: 确保后端服务已重启,并且数据库表已创建
### Q: 返回数据格式错误
A: 检查后端是否使用了最新的代码(使用 Result 类)
### Q: 无法创建凭证
A: 检查数据库连接和表结构是否正确
## 验证清单
- [ ] 数据库表 `amt_credential` 已创建
- [ ] 后端服务已重启
- [ ] 浏览器缓存已清除
- [ ] 可以访问 AMT 设置页面
- [ ] 可以查看凭证列表
- [ ] 可以添加新凭证
- [ ] 可以编辑凭证
- [ ] 可以删除凭证
- [ ] 可以测试凭证连接
- [ ] 可以批量测试凭证

View File

@ -0,0 +1,155 @@
package com.soybean.admin.controller;
import com.soybean.admin.common.Result;
import com.soybean.admin.dto.AmtCredentialDTO;
import com.soybean.admin.entity.AmtCredential;
import com.soybean.admin.service.AmtCredentialService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/amt/credential")
public class AmtCredentialController {
@Autowired
private AmtCredentialService amtCredentialService;
/**
* 分页查询凭证列表
*/
@GetMapping("/list")
public Result<Map<String, Object>> getCredentialList(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String credentialName,
@RequestParam(required = false) Integer status
) {
try {
Map<String, Object> data = amtCredentialService.getCredentialList(page, size, credentialName, status);
return Result.success(data);
} catch (Exception e) {
return Result.error("查询失败: " + e.getMessage());
}
}
/**
* 获取所有启用的凭证
*/
@GetMapping("/all")
public Result<List<AmtCredential>> getAllActiveCredentials() {
try {
List<AmtCredential> credentials = amtCredentialService.getAllActiveCredentials();
return Result.success(credentials);
} catch (Exception e) {
return Result.error("查询失败: " + e.getMessage());
}
}
/**
* 根据ID获取凭证
*/
@GetMapping("/{credentialId}")
public Result<AmtCredential> getCredentialById(@PathVariable String credentialId) {
try {
AmtCredential credential = amtCredentialService.getCredentialById(credentialId);
if (credential == null) {
return Result.error("凭证不存在");
}
return Result.success(credential);
} catch (Exception e) {
return Result.error("查询失败: " + e.getMessage());
}
}
/**
* 创建凭证
*/
@PostMapping
public Result<AmtCredential> createCredential(
@RequestBody AmtCredentialDTO dto,
HttpServletRequest request
) {
try {
String userId = (String) request.getAttribute("userId");
if (userId == null) {
userId = "admin"; // 默认用户
}
AmtCredential credential = amtCredentialService.createCredential(dto, userId);
return Result.success(credential);
} catch (Exception e) {
return Result.error("创建失败: " + e.getMessage());
}
}
/**
* 更新凭证
*/
@PutMapping("/{credentialId}")
public Result<AmtCredential> updateCredential(
@PathVariable String credentialId,
@RequestBody AmtCredentialDTO dto
) {
try {
AmtCredential credential = amtCredentialService.updateCredential(credentialId, dto);
return Result.success(credential);
} catch (Exception e) {
return Result.error("更新失败: " + e.getMessage());
}
}
/**
* 删除凭证
*/
@DeleteMapping("/{credentialId}")
public Result<Boolean> deleteCredential(@PathVariable String credentialId) {
try {
amtCredentialService.deleteCredential(credentialId);
return Result.success(true);
} catch (Exception e) {
return Result.error("删除失败: " + e.getMessage());
}
}
/**
* 设置默认凭证
*/
@PutMapping("/{credentialId}/default")
public Result<Boolean> setDefaultCredential(@PathVariable String credentialId) {
try {
amtCredentialService.setDefaultCredential(credentialId);
return Result.success(true);
} catch (Exception e) {
return Result.error("设置失败: " + e.getMessage());
}
}
/**
* 测试凭证连接
*/
@PostMapping("/{credentialId}/test")
public Result<Map<String, Object>> testCredential(@PathVariable String credentialId) {
try {
Map<String, Object> testResult = amtCredentialService.testCredential(credentialId);
return Result.success(testResult);
} catch (Exception e) {
return Result.error("测试失败: " + e.getMessage());
}
}
/**
* 批量测试凭证
*/
@PostMapping("/batch-test")
public Result<Map<String, Object>> batchTestCredentials(@RequestBody List<String> credentialIds) {
try {
Map<String, Object> testResult = amtCredentialService.batchTestCredentials(credentialIds);
return Result.success(testResult);
} catch (Exception e) {
return Result.error("批量测试失败: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,14 @@
package com.soybean.admin.dto;
import lombok.Data;
@Data
public class AmtCredentialDTO {
private String credentialId;
private String credentialName;
private String username;
private String password;
private String description;
private Boolean isDefault;
private Integer status;
}

View File

@ -0,0 +1,33 @@
package com.soybean.admin.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("amt_credential")
public class AmtCredential {
@TableId(type = IdType.ASSIGN_ID)
private String credentialId;
private String credentialName;
private String username;
private String password;
private String description;
private Boolean isDefault;
private Integer status;
private String createdBy;
private LocalDateTime createdTime;
private LocalDateTime updatedTime;
}

View File

@ -0,0 +1,9 @@
package com.soybean.admin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.soybean.admin.entity.AmtCredential;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface AmtCredentialMapper extends BaseMapper<AmtCredential> {
}

View File

@ -0,0 +1,213 @@
package com.soybean.admin.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.soybean.admin.dto.AmtCredentialDTO;
import com.soybean.admin.entity.AmtCredential;
import com.soybean.admin.mapper.AmtCredentialMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@Service
public class AmtCredentialService {
@Autowired
private AmtCredentialMapper amtCredentialMapper;
/**
* 分页查询凭证列表
*/
public Map<String, Object> getCredentialList(int page, int size, String credentialName, Integer status) {
Page<AmtCredential> pageObj = new Page<>(page, size);
LambdaQueryWrapper<AmtCredential> wrapper = new LambdaQueryWrapper<>();
if (credentialName != null && !credentialName.isEmpty()) {
wrapper.like(AmtCredential::getCredentialName, credentialName);
}
if (status != null) {
wrapper.eq(AmtCredential::getStatus, status);
}
wrapper.orderByDesc(AmtCredential::getIsDefault)
.orderByDesc(AmtCredential::getCreatedTime);
Page<AmtCredential> result = amtCredentialMapper.selectPage(pageObj, wrapper);
Map<String, Object> response = new HashMap<>();
response.put("records", result.getRecords());
response.put("total", result.getTotal());
response.put("current", result.getCurrent());
response.put("size", result.getSize());
return response;
}
/**
* 获取所有启用的凭证
*/
public List<AmtCredential> getAllActiveCredentials() {
LambdaQueryWrapper<AmtCredential> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(AmtCredential::getStatus, 1)
.orderByDesc(AmtCredential::getIsDefault)
.orderByDesc(AmtCredential::getCreatedTime);
return amtCredentialMapper.selectList(wrapper);
}
/**
* 根据ID获取凭证
*/
public AmtCredential getCredentialById(String credentialId) {
return amtCredentialMapper.selectById(credentialId);
}
/**
* 创建凭证
*/
@Transactional
public AmtCredential createCredential(AmtCredentialDTO dto, String userId) {
AmtCredential credential = new AmtCredential();
BeanUtils.copyProperties(dto, credential);
credential.setCredentialId(UUID.randomUUID().toString());
credential.setCreatedBy(userId);
// 如果设置为默认凭证取消其他凭证的默认状态
if (Boolean.TRUE.equals(dto.getIsDefault())) {
clearDefaultCredential();
}
amtCredentialMapper.insert(credential);
return credential;
}
/**
* 更新凭证
*/
@Transactional
public AmtCredential updateCredential(String credentialId, AmtCredentialDTO dto) {
AmtCredential credential = amtCredentialMapper.selectById(credentialId);
if (credential == null) {
throw new RuntimeException("凭证不存在");
}
BeanUtils.copyProperties(dto, credential);
credential.setCredentialId(credentialId);
// 如果设置为默认凭证取消其他凭证的默认状态
if (Boolean.TRUE.equals(dto.getIsDefault())) {
clearDefaultCredential();
}
amtCredentialMapper.updateById(credential);
return credential;
}
/**
* 删除凭证
*/
@Transactional
public void deleteCredential(String credentialId) {
AmtCredential credential = amtCredentialMapper.selectById(credentialId);
if (credential == null) {
throw new RuntimeException("凭证不存在");
}
if (Boolean.TRUE.equals(credential.getIsDefault())) {
throw new RuntimeException("默认凭证不能删除");
}
amtCredentialMapper.deleteById(credentialId);
}
/**
* 设置默认凭证
*/
@Transactional
public void setDefaultCredential(String credentialId) {
AmtCredential credential = amtCredentialMapper.selectById(credentialId);
if (credential == null) {
throw new RuntimeException("凭证不存在");
}
// 清除其他默认凭证
clearDefaultCredential();
// 设置当前凭证为默认
credential.setIsDefault(true);
amtCredentialMapper.updateById(credential);
}
/**
* 测试凭证连接
*/
public Map<String, Object> testCredential(String credentialId) {
AmtCredential credential = amtCredentialMapper.selectById(credentialId);
if (credential == null) {
throw new RuntimeException("凭证不存在");
}
Map<String, Object> result = new HashMap<>();
// 模拟测试连接实际应该调用 AMT SDK
try {
// 这里应该实际测试 AMT 连接
// 暂时返回模拟结果
result.put("success", true);
result.put("message", "凭证测试成功");
result.put("details", "用户名: " + credential.getUsername());
} catch (Exception e) {
result.put("success", false);
result.put("message", "凭证测试失败: " + e.getMessage());
}
return result;
}
/**
* 批量测试凭证
*/
public Map<String, Object> batchTestCredentials(List<String> credentialIds) {
Map<String, Object> result = new HashMap<>();
int successCount = 0;
int failCount = 0;
for (String credentialId : credentialIds) {
try {
Map<String, Object> testResult = testCredential(credentialId);
if (Boolean.TRUE.equals(testResult.get("success"))) {
successCount++;
} else {
failCount++;
}
} catch (Exception e) {
failCount++;
}
}
result.put("total", credentialIds.size());
result.put("success", successCount);
result.put("fail", failCount);
return result;
}
/**
* 清除所有默认凭证标记
*/
private void clearDefaultCredential() {
LambdaQueryWrapper<AmtCredential> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(AmtCredential::getIsDefault, true);
List<AmtCredential> defaultCredentials = amtCredentialMapper.selectList(wrapper);
for (AmtCredential cred : defaultCredentials) {
cred.setIsDefault(false);
amtCredentialMapper.updateById(cred);
}
}
}

View File

@ -0,0 +1,28 @@
-- 创建 AMT 凭证管理表
USE soybean_admin;
-- 删除旧表(如果存在)
DROP TABLE IF EXISTS amt_credential;
-- 创建 AMT 凭证表
CREATE TABLE amt_credential (
credential_id VARCHAR(50) PRIMARY KEY COMMENT '凭证ID',
credential_name VARCHAR(100) NOT NULL COMMENT '凭证名称',
username VARCHAR(100) NOT NULL COMMENT 'AMT用户名',
password VARCHAR(255) NOT NULL COMMENT 'AMT密码加密存储',
description VARCHAR(500) COMMENT '描述',
is_default TINYINT(1) DEFAULT 0 COMMENT '是否默认凭证',
status TINYINT(1) DEFAULT 1 COMMENT '状态1-启用0-禁用',
created_by VARCHAR(50) COMMENT '创建人',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AMT凭证管理表';
-- 插入默认凭证
INSERT INTO amt_credential (credential_id, credential_name, username, password, description, is_default, status, created_by)
VALUES
('default_admin', '默认管理员凭证', 'admin', 'admin', 'Intel AMT 默认管理员账户', 1, 1, 'system'),
('default_user', '默认用户凭证', 'user', 'user123', 'Intel AMT 默认用户账户', 0, 1, 'system');
-- 验证
SELECT * FROM amt_credential;

View File

@ -0,0 +1,20 @@
@echo off
chcp 65001 >nul
echo ========================================
echo 初始化 AMT 凭证管理表
echo ========================================
echo.
mysql -uroot -proot -hlocalhost -P3306 < backend/src/main/resources/sql/create_amt_credential_table.sql
echo.
echo ========================================
echo 初始化完成!
echo ========================================
echo.
echo 下一步操作:
echo 1. 重启后端服务
echo 2. 访问"系统设置 > AMT设置"页面
echo 3. 开始管理 AMT 凭证
echo.
pause

View File

@ -0,0 +1,36 @@
@echo off
chcp 65001 >nul
echo ========================================
echo 重新编译并启动后端服务
echo ========================================
echo.
cd backend
echo 1. 清理旧的编译文件...
call mvn clean
echo.
echo 2. 编译项目...
call mvn compile
echo.
echo 3. 打包项目...
call mvn package -DskipTests
echo.
echo 4. 启动服务...
echo 请在新窗口中运行: cd backend && mvn spring-boot:run
echo.
cd ..
echo ========================================
echo 编译完成!
echo ========================================
echo.
echo 下一步:
echo 1. 在新的命令行窗口运行: cd backend && mvn spring-boot:run
echo 2. 或者运行: start_backend.bat
echo.
pause

125
src/service/api/amt.ts Normal file
View File

@ -0,0 +1,125 @@
import { request } from '../request';
/** AMT 凭证 DTO */
export interface AmtCredential {
credentialId?: string;
credentialName: string;
username: string;
password: string;
description?: string;
isDefault?: boolean;
status?: number;
createdBy?: string;
createdTime?: string;
updatedTime?: string;
}
/** 分页查询参数 */
export interface AmtCredentialQuery {
page?: number;
size?: number;
credentialName?: string;
status?: number;
}
/** 分页响应 */
export interface AmtCredentialPageResponse {
records: AmtCredential[];
total: number;
current: number;
size: number;
}
/**
*
*/
export function fetchAmtCredentialList(params: AmtCredentialQuery) {
return request<AmtCredentialPageResponse>({
url: '/amt/credential/list',
method: 'get',
params
});
}
/**
*
*/
export function fetchAllActiveCredentials() {
return request<AmtCredential[]>({
url: '/amt/credential/all',
method: 'get'
});
}
/**
* ID获取凭证
*/
export function fetchAmtCredentialById(credentialId: string) {
return request<AmtCredential>({
url: `/amt/credential/${credentialId}`,
method: 'get'
});
}
/**
*
*/
export function createAmtCredential(data: AmtCredential) {
return request<AmtCredential>({
url: '/amt/credential',
method: 'post',
data
});
}
/**
*
*/
export function updateAmtCredential(credentialId: string, data: AmtCredential) {
return request<AmtCredential>({
url: `/amt/credential/${credentialId}`,
method: 'put',
data
});
}
/**
*
*/
export function deleteAmtCredential(credentialId: string) {
return request({
url: `/amt/credential/${credentialId}`,
method: 'delete'
});
}
/**
*
*/
export function setDefaultCredential(credentialId: string) {
return request({
url: `/amt/credential/${credentialId}/default`,
method: 'put'
});
}
/**
*
*/
export function testAmtCredential(credentialId: string) {
return request<{ success: boolean; message: string; details?: string }>({
url: `/amt/credential/${credentialId}/test`,
method: 'post'
});
}
/**
*
*/
export function batchTestCredentials(credentialIds: string[]) {
return request<{ total: number; success: number; fail: number }>({
url: '/amt/credential/batch-test',
method: 'post',
data: credentialIds
});
}

View File

@ -4,3 +4,4 @@ export * from './user';
export * from './role'; export * from './role';
export * from './permission'; export * from './permission';
export * from './device'; export * from './device';
export * from './amt';

View File

@ -66,6 +66,7 @@ declare module 'vue' {
IconMdiShieldAccount: typeof import('~icons/mdi/shield-account')['default'] IconMdiShieldAccount: typeof import('~icons/mdi/shield-account')['default']
IconMdiShieldCrown: typeof import('~icons/mdi/shield-crown')['default'] IconMdiShieldCrown: typeof import('~icons/mdi/shield-crown')['default']
IconMdiShieldStar: typeof import('~icons/mdi/shield-star')['default'] IconMdiShieldStar: typeof import('~icons/mdi/shield-star')['default']
IconMdiTestTube: typeof import('~icons/mdi/test-tube')['default']
IconMdiViewGrid: typeof import('~icons/mdi/view-grid')['default'] IconMdiViewGrid: typeof import('~icons/mdi/view-grid')['default']
IconMdiViewList: typeof import('~icons/mdi/view-list')['default'] IconMdiViewList: typeof import('~icons/mdi/view-list')['default']
IconMdiWrench: typeof import('~icons/mdi/wrench')['default'] IconMdiWrench: typeof import('~icons/mdi/wrench')['default']
@ -203,6 +204,7 @@ declare global {
const IconMdiShieldAccount: typeof import('~icons/mdi/shield-account')['default'] const IconMdiShieldAccount: typeof import('~icons/mdi/shield-account')['default']
const IconMdiShieldCrown: typeof import('~icons/mdi/shield-crown')['default'] const IconMdiShieldCrown: typeof import('~icons/mdi/shield-crown')['default']
const IconMdiShieldStar: typeof import('~icons/mdi/shield-star')['default'] const IconMdiShieldStar: typeof import('~icons/mdi/shield-star')['default']
const IconMdiTestTube: typeof import('~icons/mdi/test-tube')['default']
const IconMdiViewGrid: typeof import('~icons/mdi/view-grid')['default'] const IconMdiViewGrid: typeof import('~icons/mdi/view-grid')['default']
const IconMdiViewList: typeof import('~icons/mdi/view-list')['default'] const IconMdiViewList: typeof import('~icons/mdi/view-list')['default']
const IconMdiWrench: typeof import('~icons/mdi/wrench')['default'] const IconMdiWrench: typeof import('~icons/mdi/wrench')['default']

View File

@ -1,28 +1,361 @@
<template> <template>
<div class="h-full"> <div class="h-full p-16px">
<n-card title="AMT设置" :bordered="false" class="h-full rounded-8px shadow-sm">
<n-space vertical :size="16"> <n-space vertical :size="16">
<n-alert type="info" title="功能说明"> <!-- 功能说明 -->
Intel AMT (Active Management Technology) 配置 <n-card title="AMT 批量管理凭证" :bordered="false" class="rounded-8px shadow-sm">
<n-alert type="info" title="功能说明" class="mb-16px">
管理 Intel AMT 设备的登录凭证支持批量测试和应用到设备
</n-alert> </n-alert>
<n-form label-placement="left" label-width="120px">
<n-form-item label="AMT服务器"> <!-- 操作按钮 -->
<n-input placeholder="请输入AMT服务器地址" /> <n-space class="mb-16px">
<n-button type="primary" @click="handleAdd">
<template #icon><icon-mdi-plus /></template>
添加凭证
</n-button>
<n-button @click="handleBatchTest" :disabled="checkedRowKeys.length === 0">
<template #icon><icon-mdi-test-tube /></template>
批量测试
</n-button>
<n-button @click="loadData">
<template #icon><icon-mdi-refresh /></template>
刷新
</n-button>
</n-space>
<!-- 凭证列表 -->
<n-data-table
:columns="columns"
:data="dataSource"
:loading="loading"
:pagination="pagination"
:row-key="(row: AmtCredential) => row.credentialId!"
@update:checked-row-keys="handleCheck"
/>
</n-card>
</n-space>
<!-- 添加/编辑凭证弹窗 -->
<n-modal
v-model:show="modalVisible"
:title="modalTitle"
preset="card"
class="w-600px"
:segmented="{ content: true }"
>
<n-form
ref="formRef"
:model="formModel"
:rules="formRules"
label-placement="left"
label-width="120px"
>
<n-form-item label="凭证名称" path="credentialName">
<n-input v-model:value="formModel.credentialName" placeholder="请输入凭证名称" />
</n-form-item> </n-form-item>
<n-form-item label="端口"> <n-form-item label="用户名" path="username">
<n-input-number placeholder="16992" class="w-full" /> <n-input v-model:value="formModel.username" placeholder="请输入AMT用户名" />
</n-form-item> </n-form-item>
<n-form-item label="启用状态"> <n-form-item label="密码" path="password">
<n-switch /> <n-input
v-model:value="formModel.password"
type="password"
show-password-on="click"
placeholder="请输入AMT密码"
/>
</n-form-item>
<n-form-item label="描述" path="description">
<n-input
v-model:value="formModel.description"
type="textarea"
placeholder="请输入描述信息"
:rows="3"
/>
</n-form-item>
<n-form-item label="设为默认" path="isDefault">
<n-switch v-model:value="formModel.isDefault" />
<span class="ml-8px text-gray-500">默认凭证将优先用于新设备</span>
</n-form-item>
<n-form-item label="状态" path="status">
<n-switch :value="formModel.status === 1" @update:value="(val) => formModel.status = val ? 1 : 0" />
<span class="ml-8px text-gray-500">{{ formModel.status === 1 ? '启用' : '禁用' }}</span>
</n-form-item> </n-form-item>
</n-form> </n-form>
<template #footer>
<n-space justify="end">
<n-button @click="modalVisible = false">取消</n-button>
<n-button type="primary" @click="handleSubmit" :loading="submitLoading">
确定
</n-button>
</n-space> </n-space>
</n-card> </template>
</n-modal>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="tsx">
import { ref, reactive, onMounted } from 'vue';
import type { DataTableColumns, FormInst, FormRules } from 'naive-ui';
import { NButton, NSpace, NTag, NPopconfirm } from 'naive-ui';
import type { AmtCredential } from '@/service/api/amt';
import {
fetchAmtCredentialList,
createAmtCredential,
updateAmtCredential,
deleteAmtCredential,
setDefaultCredential,
testAmtCredential,
batchTestCredentials
} from '@/service/api/amt';
defineOptions({ defineOptions({
name: 'SystemAmt' name: 'SystemAmt'
}); });
const loading = ref(false);
const dataSource = ref<AmtCredential[]>([]);
const checkedRowKeys = ref<string[]>([]);
//
const pagination = reactive({
page: 1,
pageSize: 10,
itemCount: 0,
showSizePicker: true,
pageSizes: [10, 20, 50],
onChange: (page: number) => {
pagination.page = page;
loadData();
},
onUpdatePageSize: (pageSize: number) => {
pagination.pageSize = pageSize;
pagination.page = 1;
loadData();
}
});
//
const columns: DataTableColumns<AmtCredential> = [
{ type: 'selection' },
{
title: '凭证名称',
key: 'credentialName',
width: 180,
ellipsis: { tooltip: true }
},
{
title: '用户名',
key: 'username',
width: 150
},
{
title: '密码',
key: 'password',
width: 120,
render: () => '••••••••'
},
{
title: '描述',
key: 'description',
ellipsis: { tooltip: true }
},
{
title: '默认',
key: 'isDefault',
width: 80,
render: (row) => (
<NTag type={row.isDefault ? 'success' : 'default'} size="small">
{row.isDefault ? '是' : '否'}
</NTag>
)
},
{
title: '状态',
key: 'status',
width: 80,
render: (row) => (
<NTag type={row.status === 1 ? 'success' : 'error'} size="small">
{row.status === 1 ? '启用' : '禁用'}
</NTag>
)
},
{
title: '操作',
key: 'actions',
width: 280,
fixed: 'right',
render: (row) => (
<NSpace>
<NButton size="small" onClick={() => handleTest(row.credentialId!)}>
测试
</NButton>
{!row.isDefault && (
<NButton size="small" onClick={() => handleSetDefault(row.credentialId!)}>
设为默认
</NButton>
)}
<NButton size="small" onClick={() => handleEdit(row)}>
编辑
</NButton>
<NPopconfirm onPositiveClick={() => handleDelete(row.credentialId!)}>
{{
default: () => '确定删除此凭证吗?',
trigger: () => (
<NButton size="small" type="error" disabled={row.isDefault}>
删除
</NButton>
)
}}
</NPopconfirm>
</NSpace>
)
}
];
//
const formRef = ref<FormInst | null>(null);
const modalVisible = ref(false);
const modalTitle = ref('');
const submitLoading = ref(false);
const editingId = ref<string | null>(null);
const formModel = reactive<AmtCredential>({
credentialName: '',
username: '',
password: '',
description: '',
isDefault: false,
status: 1
});
const formRules: FormRules = {
credentialName: [{ required: true, message: '请输入凭证名称', trigger: 'blur' }],
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
};
//
async function loadData() {
loading.value = true;
try {
const { data } = await fetchAmtCredentialList({
page: pagination.page,
size: pagination.pageSize
});
dataSource.value = data.records;
pagination.itemCount = data.total;
} catch (error) {
window.$message?.error('加载数据失败');
} finally {
loading.value = false;
}
}
//
function handleAdd() {
modalTitle.value = '添加凭证';
editingId.value = null;
Object.assign(formModel, {
credentialName: '',
username: '',
password: '',
description: '',
isDefault: false,
status: 1
});
modalVisible.value = true;
}
//
function handleEdit(row: AmtCredential) {
modalTitle.value = '编辑凭证';
editingId.value = row.credentialId!;
Object.assign(formModel, row);
modalVisible.value = true;
}
//
async function handleSubmit() {
await formRef.value?.validate();
submitLoading.value = true;
try {
if (editingId.value) {
await updateAmtCredential(editingId.value, formModel);
window.$message?.success('更新成功');
} else {
await createAmtCredential(formModel);
window.$message?.success('创建成功');
}
modalVisible.value = false;
loadData();
} catch (error: any) {
window.$message?.error(error.message || '操作失败');
} finally {
submitLoading.value = false;
}
}
//
async function handleDelete(credentialId: string) {
try {
await deleteAmtCredential(credentialId);
window.$message?.success('删除成功');
loadData();
} catch (error: any) {
window.$message?.error(error.message || '删除失败');
}
}
//
async function handleSetDefault(credentialId: string) {
try {
await setDefaultCredential(credentialId);
window.$message?.success('设置成功');
loadData();
} catch (error: any) {
window.$message?.error(error.message || '设置失败');
}
}
//
async function handleTest(credentialId: string) {
const loadingMsg = window.$message?.loading('正在测试连接...', { duration: 0 });
try {
const { data } = await testAmtCredential(credentialId);
loadingMsg?.destroy();
if (data.success) {
window.$message?.success(data.message);
} else {
window.$message?.error(data.message);
}
} catch (error: any) {
loadingMsg?.destroy();
window.$message?.error(error.message || '测试失败');
}
}
//
async function handleBatchTest() {
const loadingMsg = window.$message?.loading('正在批量测试...', { duration: 0 });
try {
const { data } = await batchTestCredentials(checkedRowKeys.value);
loadingMsg?.destroy();
window.$message?.success(
`测试完成:成功 ${data.success} 个,失败 ${data.fail}`
);
} catch (error: any) {
loadingMsg?.destroy();
window.$message?.error(error.message || '批量测试失败');
}
}
//
function handleCheck(keys: string[]) {
checkedRowKeys.value = keys;
}
onMounted(() => {
loadData();
});
</script> </script>

16
test_amt_api.bat Normal file
View File

@ -0,0 +1,16 @@
@echo off
chcp 65001 >nul
echo ========================================
echo 测试 AMT API 接口
echo ========================================
echo.
echo 测试获取凭证列表...
curl -X GET "http://localhost:8080/api/amt/credential/list?page=1&size=10" -H "Content-Type: application/json"
echo.
echo.
echo ========================================
echo 测试完成
echo ========================================
pause