Compare commits
No commits in common. "bd64e889fd850fc4326770c56ad1bc41bce06b27" and "c546d4635a1af8c6c5fa4e1a2f9caae4ab167566" have entirely different histories.
bd64e889fd
...
c546d4635a
@ -311,83 +311,3 @@ export const remoteDesktopApi = {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 操作系统设备 API
|
|
||||||
export const osDeviceApi = {
|
|
||||||
// 获取所有操作系统设备
|
|
||||||
getAll() {
|
|
||||||
return request.get<any[]>({
|
|
||||||
url: '/api/os-devices'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// 获取单个设备
|
|
||||||
getById(id: number) {
|
|
||||||
return request.get({
|
|
||||||
url: `/api/os-devices/${id}`
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// 启动操作系统扫描
|
|
||||||
startScan(networkSegment: string, subnetMask: string) {
|
|
||||||
return request.post({
|
|
||||||
url: '/api/os-devices/scan/start',
|
|
||||||
params: { networkSegment, subnetMask }
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// 获取扫描状态
|
|
||||||
getScanStatus(taskId: string) {
|
|
||||||
return request.get({
|
|
||||||
url: `/api/os-devices/scan/status/${taskId}`
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// 取消扫描
|
|
||||||
cancelScan(taskId: string) {
|
|
||||||
return request.post({
|
|
||||||
url: `/api/os-devices/scan/cancel/${taskId}`
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// 获取设备详细信息(通过 WMI)
|
|
||||||
fetchInfo(id: number, credentials: { username: string; password: string }) {
|
|
||||||
return request.post({
|
|
||||||
url: `/api/os-devices/${id}/fetch-info`,
|
|
||||||
data: credentials,
|
|
||||||
showSuccessMessage: true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// 手动绑定 AMT 设备
|
|
||||||
bindAmt(id: number, amtDeviceId: number) {
|
|
||||||
return request.post({
|
|
||||||
url: `/api/os-devices/${id}/bind-amt/${amtDeviceId}`,
|
|
||||||
showSuccessMessage: true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// 解除 AMT 绑定
|
|
||||||
unbindAmt(id: number) {
|
|
||||||
return request.post({
|
|
||||||
url: `/api/os-devices/${id}/unbind-amt`,
|
|
||||||
showSuccessMessage: true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// 自动绑定所有设备
|
|
||||||
autoBind() {
|
|
||||||
return request.post({
|
|
||||||
url: '/api/os-devices/auto-bind',
|
|
||||||
showSuccessMessage: true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// 删除设备
|
|
||||||
delete(id: number) {
|
|
||||||
return request.del({
|
|
||||||
url: `/api/os-devices/${id}`,
|
|
||||||
showSuccessMessage: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -58,65 +58,6 @@ export function fetchGetRoleList(params: Api.SystemManage.RoleSearchParams) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取所有角色(下拉选择用)
|
|
||||||
export function fetchGetAllRoles() {
|
|
||||||
return request.get<{ roleId: number; roleName: string; roleCode: string }[]>({
|
|
||||||
url: '/api/role/all'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建角色
|
|
||||||
export function fetchCreateRole(data: {
|
|
||||||
roleName: string
|
|
||||||
roleCode: string
|
|
||||||
description?: string
|
|
||||||
enabled?: boolean
|
|
||||||
}) {
|
|
||||||
return request.post({
|
|
||||||
url: '/api/role',
|
|
||||||
params: data,
|
|
||||||
showSuccessMessage: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新角色
|
|
||||||
export function fetchUpdateRole(id: number, data: {
|
|
||||||
roleName?: string
|
|
||||||
roleCode?: string
|
|
||||||
description?: string
|
|
||||||
enabled?: boolean
|
|
||||||
}) {
|
|
||||||
return request.put({
|
|
||||||
url: `/api/role/${id}`,
|
|
||||||
params: data,
|
|
||||||
showSuccessMessage: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除角色
|
|
||||||
export function fetchDeleteRole(id: number) {
|
|
||||||
return request.del({
|
|
||||||
url: `/api/role/${id}`,
|
|
||||||
showSuccessMessage: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取角色的菜单权限
|
|
||||||
export function fetchGetRoleMenus(roleId: number) {
|
|
||||||
return request.get<number[]>({
|
|
||||||
url: `/api/role/${roleId}/menus`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置角色的菜单权限
|
|
||||||
export function fetchSetRoleMenus(roleId: number, menuIds: number[]) {
|
|
||||||
return request.put({
|
|
||||||
url: `/api/role/${roleId}/menus`,
|
|
||||||
params: { menuIds },
|
|
||||||
showSuccessMessage: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取菜单列表
|
// 获取菜单列表
|
||||||
export function fetchGetMenuList() {
|
export function fetchGetMenuList() {
|
||||||
return request.get<AppRouteRecord[]>({
|
return request.get<AppRouteRecord[]>({
|
||||||
|
|||||||
@ -28,23 +28,6 @@
|
|||||||
|
|
||||||
<ElTable :data="devices" v-loading="loading" stripe style="width: 100%">
|
<ElTable :data="devices" v-loading="loading" stripe style="width: 100%">
|
||||||
<ElTableColumn prop="ipAddress" label="IP 地址" width="140" />
|
<ElTableColumn prop="ipAddress" label="IP 地址" width="140" />
|
||||||
<ElTableColumn prop="systemUuid" label="系统 UUID" width="360">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<div style="display: flex; align-items: center; gap: 8px;">
|
|
||||||
<span v-if="row.systemUuid" style="font-family: monospace; font-size: 12px;">{{ row.systemUuid }}</span>
|
|
||||||
<ElTag v-else type="info" size="small">未获取</ElTag>
|
|
||||||
<ElButton
|
|
||||||
type="primary"
|
|
||||||
size="small"
|
|
||||||
link
|
|
||||||
@click="handleFetchUuid(row)"
|
|
||||||
:loading="row.fetchingUuid"
|
|
||||||
>
|
|
||||||
{{ row.systemUuid ? '刷新' : '获取' }}
|
|
||||||
</ElButton>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</ElTableColumn>
|
|
||||||
<ElTableColumn label="AMT 版本" width="100">
|
<ElTableColumn label="AMT 版本" width="100">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
{{ row.majorVersion }}.{{ row.minorVersion }}
|
{{ row.majorVersion }}.{{ row.minorVersion }}
|
||||||
@ -147,7 +130,7 @@
|
|||||||
import { ref, onMounted, onUnmounted } from 'vue'
|
import { ref, onMounted, onUnmounted } from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { Search, Refresh, ArrowDown, VideoPlay, VideoPause, RefreshRight, CircleClose } from '@element-plus/icons-vue'
|
import { Search, Refresh, ArrowDown, VideoPlay, VideoPause, RefreshRight, CircleClose } from '@element-plus/icons-vue'
|
||||||
import { deviceApi, powerApi, hardwareApi } from '@/api/amt'
|
import { deviceApi, powerApi } from '@/api/amt'
|
||||||
import HardwareInfoModal from './modules/hardware-info-modal.vue'
|
import HardwareInfoModal from './modules/hardware-info-modal.vue'
|
||||||
import RemoteDesktopModal from './modules/remote-desktop-modal.vue'
|
import RemoteDesktopModal from './modules/remote-desktop-modal.vue'
|
||||||
|
|
||||||
@ -308,24 +291,6 @@ const saveCredentials = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleFetchUuid = async (device: any) => {
|
|
||||||
device.fetchingUuid = true
|
|
||||||
try {
|
|
||||||
// 强制刷新硬件信息以获取 UUID
|
|
||||||
const hardwareInfo = await hardwareApi.getHardwareInfo(device.id, true)
|
|
||||||
if (hardwareInfo.systemInfo?.uuid) {
|
|
||||||
device.systemUuid = hardwareInfo.systemInfo.uuid
|
|
||||||
ElMessage.success('UUID 获取成功')
|
|
||||||
} else {
|
|
||||||
ElMessage.warning('未能从设备获取 UUID')
|
|
||||||
}
|
|
||||||
} catch (error: any) {
|
|
||||||
ElMessage.error('获取 UUID 失败: ' + (error.message || '未知错误'))
|
|
||||||
} finally {
|
|
||||||
device.fetchingUuid = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handlePowerCommand = async (command: string, device: any) => {
|
const handlePowerCommand = async (command: string, device: any) => {
|
||||||
const actionMap: Record<string, { api: Function; name: string; confirmMsg: string }> = {
|
const actionMap: Record<string, { api: Function; name: string; confirmMsg: string }> = {
|
||||||
'power-on': { api: powerApi.powerOn, name: '开机', confirmMsg: '确定要开机吗?' },
|
'power-on': { api: powerApi.powerOn, name: '开机', confirmMsg: '确定要开机吗?' },
|
||||||
|
|||||||
@ -29,12 +29,9 @@
|
|||||||
<ElDescriptionsItem label="型号" v-if="hardwareInfo.systemInfo?.model">
|
<ElDescriptionsItem label="型号" v-if="hardwareInfo.systemInfo?.model">
|
||||||
{{ hardwareInfo.systemInfo.model }}
|
{{ hardwareInfo.systemInfo.model }}
|
||||||
</ElDescriptionsItem>
|
</ElDescriptionsItem>
|
||||||
<ElDescriptionsItem label="序列号" v-if="hardwareInfo.systemInfo?.serialNumber">
|
<ElDescriptionsItem label="序列号" v-if="hardwareInfo.systemInfo?.serialNumber" :span="2">
|
||||||
{{ hardwareInfo.systemInfo.serialNumber }}
|
{{ hardwareInfo.systemInfo.serialNumber }}
|
||||||
</ElDescriptionsItem>
|
</ElDescriptionsItem>
|
||||||
<ElDescriptionsItem label="系统 UUID" v-if="hardwareInfo.systemInfo?.uuid">
|
|
||||||
{{ hardwareInfo.systemInfo.uuid }}
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
</ElDescriptions>
|
</ElDescriptions>
|
||||||
|
|
||||||
<!-- CPU 信息 -->
|
<!-- CPU 信息 -->
|
||||||
|
|||||||
@ -95,19 +95,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 远程桌面 iframe -->
|
<!-- 远程桌面 iframe -->
|
||||||
<div v-else class="remote-desktop-container" @click="focusIframe">
|
<div v-else class="remote-desktop-container">
|
||||||
<div class="focus-hint" v-if="showFocusHint">
|
<iframe ref="rdpFrame" :src="connectionUrl" class="rdp-iframe" allowfullscreen />
|
||||||
<span>点击此处激活键盘输入</span>
|
|
||||||
</div>
|
|
||||||
<iframe
|
|
||||||
ref="rdpFrame"
|
|
||||||
:src="connectionUrl"
|
|
||||||
class="rdp-iframe"
|
|
||||||
allowfullscreen
|
|
||||||
@load="onIframeLoad"
|
|
||||||
@mouseenter="focusIframe"
|
|
||||||
tabindex="0"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</ElDialog>
|
</ElDialog>
|
||||||
</template>
|
</template>
|
||||||
@ -142,22 +131,6 @@ const tokenForm = ref({ expiresInMinutes: 30, maxUseCount: 1, note: '' })
|
|||||||
const generatedToken = ref<any>(null)
|
const generatedToken = ref<any>(null)
|
||||||
const deviceTokens = ref<any[]>([])
|
const deviceTokens = ref<any[]>([])
|
||||||
const loadingTokens = ref(false)
|
const loadingTokens = ref(false)
|
||||||
const showFocusHint = ref(true)
|
|
||||||
|
|
||||||
// 聚焦 iframe 以接收键盘输入
|
|
||||||
const focusIframe = () => {
|
|
||||||
if (rdpFrame.value) {
|
|
||||||
rdpFrame.value.focus()
|
|
||||||
showFocusHint.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// iframe 加载完成后自动聚焦
|
|
||||||
const onIframeLoad = () => {
|
|
||||||
setTimeout(() => {
|
|
||||||
focusIframe()
|
|
||||||
}, 500)
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadDeviceTokens = async () => {
|
const loadDeviceTokens = async () => {
|
||||||
if (!props.device?.id) return
|
if (!props.device?.id) return
|
||||||
@ -254,7 +227,6 @@ watch(() => props.modelValue, (newVal) => {
|
|||||||
connectionUrl.value = ''
|
connectionUrl.value = ''
|
||||||
generatedToken.value = null
|
generatedToken.value = null
|
||||||
activeTab.value = 'quick'
|
activeTab.value = 'quick'
|
||||||
showFocusHint.value = true
|
|
||||||
loadDeviceTokens()
|
loadDeviceTokens()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -269,10 +241,8 @@ watch(() => props.modelValue, (newVal) => {
|
|||||||
.generated-link { margin-top: 20px; }
|
.generated-link { margin-top: 20px; }
|
||||||
.link-box { margin-top: 10px; }
|
.link-box { margin-top: 10px; }
|
||||||
.tokens-list { padding: 10px; }
|
.tokens-list { padding: 10px; }
|
||||||
.remote-desktop-container { width: 100%; height: calc(85vh - 100px); min-height: 600px; display: flex; flex-direction: column; justify-content: center; align-items: center; background: #1a1a1a; position: relative; }
|
.remote-desktop-container { width: 100%; height: calc(85vh - 100px); min-height: 600px; display: flex; justify-content: center; align-items: center; background: #1a1a1a; }
|
||||||
.focus-hint { position: absolute; top: 10px; left: 50%; transform: translateX(-50%); background: rgba(0, 0, 0, 0.7); color: #fff; padding: 8px 16px; border-radius: 4px; font-size: 14px; z-index: 10; animation: fadeOut 3s forwards; }
|
.rdp-iframe { width: 100%; height: 100%; border: none; background: transparent; }
|
||||||
@keyframes fadeOut { 0% { opacity: 1; } 70% { opacity: 1; } 100% { opacity: 0; pointer-events: none; } }
|
|
||||||
.rdp-iframe { width: 100%; height: 100%; border: none; background: transparent; outline: none; }
|
|
||||||
:deep(.el-dialog__body) { padding: 0; overflow: hidden; }
|
:deep(.el-dialog__body) { padding: 0; overflow: hidden; }
|
||||||
:deep(.el-dialog__header) { padding: 15px 20px; margin: 0; border-bottom: 1px solid #e4e7ed; }
|
:deep(.el-dialog__header) { padding: 15px 20px; margin: 0; border-bottom: 1px solid #e4e7ed; }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,330 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="os-devices-page">
|
|
||||||
<!-- 工具栏 -->
|
|
||||||
<div class="toolbar">
|
|
||||||
<div class="left">
|
|
||||||
<ElButton type="primary" @click="showScanDialog = true">
|
|
||||||
<ElIcon><Search /></ElIcon>
|
|
||||||
扫描操作系统
|
|
||||||
</ElButton>
|
|
||||||
<ElButton @click="handleAutoBind" :loading="autoBinding">
|
|
||||||
<ElIcon><Link /></ElIcon>
|
|
||||||
自动绑定 AMT
|
|
||||||
</ElButton>
|
|
||||||
<ElButton @click="loadDevices" :loading="loading">
|
|
||||||
<ElIcon><Refresh /></ElIcon>
|
|
||||||
刷新
|
|
||||||
</ElButton>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<ElInput v-model="searchKeyword" placeholder="搜索 IP/主机名" clearable style="width: 200px" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 设备列表 -->
|
|
||||||
<ElTable :data="filteredDevices" v-loading="loading" stripe>
|
|
||||||
<ElTableColumn prop="ipAddress" label="IP 地址" width="140" />
|
|
||||||
<ElTableColumn prop="hostname" label="主机名" width="150" />
|
|
||||||
<ElTableColumn label="操作系统" width="200">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<ElTag :type="getOsTagType(row.osType)" size="small">{{ row.osType }}</ElTag>
|
|
||||||
<span v-if="row.osVersion" style="margin-left: 5px; font-size: 12px; color: #999">
|
|
||||||
{{ row.osVersion?.substring(0, 30) }}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</ElTableColumn>
|
|
||||||
<ElTableColumn prop="systemUuid" label="UUID" width="280">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<span v-if="row.systemUuid" style="font-family: monospace; font-size: 11px">{{ row.systemUuid }}</span>
|
|
||||||
<ElTag v-else type="warning" size="small">未获取</ElTag>
|
|
||||||
</template>
|
|
||||||
</ElTableColumn>
|
|
||||||
<ElTableColumn label="AMT 绑定" width="150">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<ElTag v-if="row.amtDeviceId" type="success" size="small">
|
|
||||||
{{ row.amtDeviceIp }}
|
|
||||||
</ElTag>
|
|
||||||
<ElTag v-else type="info" size="small">未绑定</ElTag>
|
|
||||||
</template>
|
|
||||||
</ElTableColumn>
|
|
||||||
<ElTableColumn label="状态" width="80">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<ElTag :type="row.isOnline ? 'success' : 'danger'" size="small">
|
|
||||||
{{ row.isOnline ? '在线' : '离线' }}
|
|
||||||
</ElTag>
|
|
||||||
</template>
|
|
||||||
</ElTableColumn>
|
|
||||||
<ElTableColumn label="操作" width="280" fixed="right">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<ElButton size="small" @click="handleFetchInfo(row)">获取信息</ElButton>
|
|
||||||
<ElButton size="small" @click="handleBindAmt(row)" v-if="!row.amtDeviceId">绑定 AMT</ElButton>
|
|
||||||
<ElButton size="small" type="warning" @click="handleUnbindAmt(row)" v-else>解绑</ElButton>
|
|
||||||
<ElButton size="small" type="danger" @click="handleDelete(row)">删除</ElButton>
|
|
||||||
</template>
|
|
||||||
</ElTableColumn>
|
|
||||||
</ElTable>
|
|
||||||
|
|
||||||
<!-- 扫描对话框 -->
|
|
||||||
<ElDialog v-model="showScanDialog" title="扫描操作系统" width="500px">
|
|
||||||
<ElForm :model="scanForm" label-width="100px">
|
|
||||||
<ElFormItem label="网段">
|
|
||||||
<ElInput v-model="scanForm.networkSegment" placeholder="例如: 192.168.1.0" />
|
|
||||||
</ElFormItem>
|
|
||||||
<ElFormItem label="子网掩码">
|
|
||||||
<ElSelect v-model="scanForm.subnetMask" style="width: 100%">
|
|
||||||
<ElOption label="/24 (255.255.255.0)" value="/24" />
|
|
||||||
<ElOption label="/16 (255.255.0.0)" value="/16" />
|
|
||||||
<ElOption label="/8 (255.0.0.0)" value="/8" />
|
|
||||||
</ElSelect>
|
|
||||||
</ElFormItem>
|
|
||||||
</ElForm>
|
|
||||||
<div v-if="scanning" class="scan-progress">
|
|
||||||
<ElProgress :percentage="scanProgress.progressPercentage" :format="() => `${scanProgress.scannedCount}/${scanProgress.totalCount}`" />
|
|
||||||
<p>当前扫描: {{ scanProgress.currentIp }}</p>
|
|
||||||
<p>已发现: {{ scanProgress.foundDevices }} 台设备</p>
|
|
||||||
</div>
|
|
||||||
<template #footer>
|
|
||||||
<ElButton @click="showScanDialog = false" :disabled="scanning">取消</ElButton>
|
|
||||||
<ElButton type="primary" @click="startScan" :loading="scanning">
|
|
||||||
{{ scanning ? '扫描中...' : '开始扫描' }}
|
|
||||||
</ElButton>
|
|
||||||
</template>
|
|
||||||
</ElDialog>
|
|
||||||
|
|
||||||
<!-- 获取信息对话框 -->
|
|
||||||
<ElDialog v-model="showFetchDialog" title="获取系统信息" width="400px">
|
|
||||||
<ElAlert type="info" :closable="false" style="margin-bottom: 15px">
|
|
||||||
需要提供 Windows 管理员凭据通过 WMI 获取系统信息
|
|
||||||
</ElAlert>
|
|
||||||
<ElForm :model="fetchForm" label-width="80px">
|
|
||||||
<ElFormItem label="用户名">
|
|
||||||
<ElInput v-model="fetchForm.username" placeholder="administrator" />
|
|
||||||
</ElFormItem>
|
|
||||||
<ElFormItem label="密码">
|
|
||||||
<ElInput v-model="fetchForm.password" type="password" show-password />
|
|
||||||
</ElFormItem>
|
|
||||||
</ElForm>
|
|
||||||
<template #footer>
|
|
||||||
<ElButton @click="showFetchDialog = false">取消</ElButton>
|
|
||||||
<ElButton type="primary" @click="doFetchInfo" :loading="fetching">获取</ElButton>
|
|
||||||
</template>
|
|
||||||
</ElDialog>
|
|
||||||
|
|
||||||
<!-- 绑定 AMT 对话框 -->
|
|
||||||
<ElDialog v-model="showBindDialog" title="绑定 AMT 设备" width="500px">
|
|
||||||
<ElTable :data="amtDevices" v-loading="loadingAmt" @row-click="selectAmtDevice" highlight-current-row>
|
|
||||||
<ElTableColumn prop="ipAddress" label="IP 地址" width="140" />
|
|
||||||
<ElTableColumn prop="hostname" label="主机名" />
|
|
||||||
<ElTableColumn prop="systemUuid" label="UUID" width="280">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<span v-if="row.systemUuid" style="font-family: monospace; font-size: 11px">{{ row.systemUuid }}</span>
|
|
||||||
<ElTag v-else type="warning" size="small">未获取</ElTag>
|
|
||||||
</template>
|
|
||||||
</ElTableColumn>
|
|
||||||
</ElTable>
|
|
||||||
<template #footer>
|
|
||||||
<ElButton @click="showBindDialog = false">取消</ElButton>
|
|
||||||
<ElButton type="primary" @click="doBind" :disabled="!selectedAmtDevice">绑定</ElButton>
|
|
||||||
</template>
|
|
||||||
</ElDialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, computed, onMounted } from 'vue'
|
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
||||||
import { Search, Refresh, Link } from '@element-plus/icons-vue'
|
|
||||||
import { osDeviceApi, deviceApi } from '@/api/amt'
|
|
||||||
|
|
||||||
const loading = ref(false)
|
|
||||||
const devices = ref<any[]>([])
|
|
||||||
const searchKeyword = ref('')
|
|
||||||
const autoBinding = ref(false)
|
|
||||||
|
|
||||||
// 扫描相关
|
|
||||||
const showScanDialog = ref(false)
|
|
||||||
const scanning = ref(false)
|
|
||||||
const scanForm = ref({ networkSegment: '192.168.1.0', subnetMask: '/24' })
|
|
||||||
const scanProgress = ref({ scannedCount: 0, totalCount: 0, foundDevices: 0, progressPercentage: 0, currentIp: '' })
|
|
||||||
let scanTaskId = ''
|
|
||||||
|
|
||||||
// 获取信息相关
|
|
||||||
const showFetchDialog = ref(false)
|
|
||||||
const fetching = ref(false)
|
|
||||||
const fetchForm = ref({ username: '', password: '' })
|
|
||||||
let currentFetchDevice: any = null
|
|
||||||
|
|
||||||
// 绑定相关
|
|
||||||
const showBindDialog = ref(false)
|
|
||||||
const loadingAmt = ref(false)
|
|
||||||
const amtDevices = ref<any[]>([])
|
|
||||||
const selectedAmtDevice = ref<any>(null)
|
|
||||||
let currentBindDevice: any = null
|
|
||||||
|
|
||||||
const filteredDevices = computed(() => {
|
|
||||||
if (!searchKeyword.value) return devices.value
|
|
||||||
const kw = searchKeyword.value.toLowerCase()
|
|
||||||
return devices.value.filter(d =>
|
|
||||||
d.ipAddress?.toLowerCase().includes(kw) ||
|
|
||||||
d.hostname?.toLowerCase().includes(kw)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const getOsTagType = (osType: string) => {
|
|
||||||
switch (osType) {
|
|
||||||
case 'Windows': return 'primary'
|
|
||||||
case 'Linux': return 'success'
|
|
||||||
default: return 'info'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadDevices = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
devices.value = await osDeviceApi.getAll()
|
|
||||||
} catch (error) {
|
|
||||||
console.error('加载设备失败', error)
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const startScan = async () => {
|
|
||||||
scanning.value = true
|
|
||||||
scanProgress.value = { scannedCount: 0, totalCount: 0, foundDevices: 0, progressPercentage: 0, currentIp: '' }
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await osDeviceApi.startScan(scanForm.value.networkSegment, scanForm.value.subnetMask)
|
|
||||||
scanTaskId = result.taskId
|
|
||||||
|
|
||||||
let retryCount = 0
|
|
||||||
const maxRetries = 3
|
|
||||||
|
|
||||||
// 轮询扫描进度
|
|
||||||
const pollProgress = async () => {
|
|
||||||
if (!scanning.value) return
|
|
||||||
try {
|
|
||||||
const progress = await osDeviceApi.getScanStatus(scanTaskId)
|
|
||||||
retryCount = 0 // 成功后重置重试计数
|
|
||||||
scanProgress.value = progress
|
|
||||||
|
|
||||||
if (progress.progressPercentage < 100) {
|
|
||||||
setTimeout(pollProgress, 500)
|
|
||||||
} else {
|
|
||||||
scanning.value = false
|
|
||||||
showScanDialog.value = false
|
|
||||||
ElMessage.success(`扫描完成,发现 ${progress.foundDevices} 台设备`)
|
|
||||||
loadDevices()
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
retryCount++
|
|
||||||
if (retryCount < maxRetries) {
|
|
||||||
// 重试,增加延迟
|
|
||||||
setTimeout(pollProgress, 1000)
|
|
||||||
} else {
|
|
||||||
scanning.value = false
|
|
||||||
ElMessage.error('获取扫描进度失败')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 延迟500ms后开始轮询,给后端一点时间注册任务
|
|
||||||
setTimeout(pollProgress, 500)
|
|
||||||
} catch (error) {
|
|
||||||
scanning.value = false
|
|
||||||
ElMessage.error('启动扫描失败')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleAutoBind = async () => {
|
|
||||||
autoBinding.value = true
|
|
||||||
try {
|
|
||||||
await osDeviceApi.autoBind()
|
|
||||||
loadDevices()
|
|
||||||
} finally {
|
|
||||||
autoBinding.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleFetchInfo = (row: any) => {
|
|
||||||
currentFetchDevice = row
|
|
||||||
fetchForm.value = { username: '', password: '' }
|
|
||||||
showFetchDialog.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const doFetchInfo = async () => {
|
|
||||||
if (!fetchForm.value.username || !fetchForm.value.password) {
|
|
||||||
ElMessage.warning('请输入凭据')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fetching.value = true
|
|
||||||
try {
|
|
||||||
await osDeviceApi.fetchInfo(currentFetchDevice.id, fetchForm.value)
|
|
||||||
showFetchDialog.value = false
|
|
||||||
loadDevices()
|
|
||||||
} catch (error: any) {
|
|
||||||
ElMessage.error(error.message || '获取信息失败')
|
|
||||||
} finally {
|
|
||||||
fetching.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleBindAmt = async (row: any) => {
|
|
||||||
currentBindDevice = row
|
|
||||||
selectedAmtDevice.value = null
|
|
||||||
showBindDialog.value = true
|
|
||||||
loadingAmt.value = true
|
|
||||||
try {
|
|
||||||
amtDevices.value = await deviceApi.getAllDevices()
|
|
||||||
} finally {
|
|
||||||
loadingAmt.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectAmtDevice = (row: any) => {
|
|
||||||
selectedAmtDevice.value = row
|
|
||||||
}
|
|
||||||
|
|
||||||
const doBind = async () => {
|
|
||||||
if (!selectedAmtDevice.value) return
|
|
||||||
try {
|
|
||||||
await osDeviceApi.bindAmt(currentBindDevice.id, selectedAmtDevice.value.id)
|
|
||||||
showBindDialog.value = false
|
|
||||||
loadDevices()
|
|
||||||
} catch (error) {
|
|
||||||
ElMessage.error('绑定失败')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleUnbindAmt = async (row: any) => {
|
|
||||||
try {
|
|
||||||
await ElMessageBox.confirm('确定要解除 AMT 绑定吗?', '确认')
|
|
||||||
await osDeviceApi.unbindAmt(row.id)
|
|
||||||
loadDevices()
|
|
||||||
} catch (error) {
|
|
||||||
if (error !== 'cancel') ElMessage.error('解绑失败')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleDelete = async (row: any) => {
|
|
||||||
try {
|
|
||||||
await ElMessageBox.confirm('确定要删除此设备吗?', '确认删除', { type: 'warning' })
|
|
||||||
await osDeviceApi.delete(row.id)
|
|
||||||
loadDevices()
|
|
||||||
} catch (error) {
|
|
||||||
if (error !== 'cancel') ElMessage.error('删除失败')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
loadDevices()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.os-devices-page { padding: 20px; }
|
|
||||||
.toolbar { display: flex; justify-content: space-between; margin-bottom: 20px; }
|
|
||||||
.toolbar .left { display: flex; gap: 10px; }
|
|
||||||
.scan-progress { margin-top: 20px; text-align: center; }
|
|
||||||
.scan-progress p { margin: 10px 0; color: #666; }
|
|
||||||
</style>
|
|
||||||
@ -39,7 +39,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 远程桌面 iframe -->
|
<!-- 远程桌面 iframe -->
|
||||||
<div v-else class="remote-desktop-container" @click="focusIframe">
|
<div v-else class="remote-desktop-container">
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<span>远程桌面 - {{ tokenInfo?.deviceIp }}</span>
|
<span>远程桌面 - {{ tokenInfo?.deviceIp }}</span>
|
||||||
<div class="toolbar-actions">
|
<div class="toolbar-actions">
|
||||||
@ -49,18 +49,7 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="focus-hint" v-if="showFocusHint">
|
<iframe ref="rdpFrame" :src="connectionUrl" class="rdp-iframe" allowfullscreen />
|
||||||
<span>点击此处激活键盘输入</span>
|
|
||||||
</div>
|
|
||||||
<iframe
|
|
||||||
ref="rdpFrame"
|
|
||||||
:src="connectionUrl"
|
|
||||||
class="rdp-iframe"
|
|
||||||
allowfullscreen
|
|
||||||
@load="onIframeLoad"
|
|
||||||
@mouseenter="focusIframe"
|
|
||||||
tabindex="0"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -84,22 +73,6 @@ const connectionUrl = ref('')
|
|||||||
const connecting = ref(false)
|
const connecting = ref(false)
|
||||||
const isFullscreen = ref(false)
|
const isFullscreen = ref(false)
|
||||||
const rdpFrame = ref<HTMLIFrameElement | null>(null)
|
const rdpFrame = ref<HTMLIFrameElement | null>(null)
|
||||||
const showFocusHint = ref(true)
|
|
||||||
|
|
||||||
// 聚焦 iframe 以接收键盘输入
|
|
||||||
const focusIframe = () => {
|
|
||||||
if (rdpFrame.value) {
|
|
||||||
rdpFrame.value.focus()
|
|
||||||
showFocusHint.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// iframe 加载完成后自动聚焦
|
|
||||||
const onIframeLoad = () => {
|
|
||||||
setTimeout(() => {
|
|
||||||
focusIframe()
|
|
||||||
}, 500)
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const token = route.params.token as string
|
const token = route.params.token as string
|
||||||
@ -210,7 +183,6 @@ const formatDate = (dateStr: string) => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: #1a1a1a;
|
background: #1a1a1a;
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
@ -232,26 +204,5 @@ const formatDate = (dateStr: string) => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
border: none;
|
border: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.focus-hint {
|
|
||||||
position: absolute;
|
|
||||||
top: 60px;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
background: rgba(0, 0, 0, 0.7);
|
|
||||||
color: #fff;
|
|
||||||
padding: 8px 16px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 14px;
|
|
||||||
z-index: 10;
|
|
||||||
animation: fadeOut 3s forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadeOut {
|
|
||||||
0% { opacity: 1; }
|
|
||||||
70% { opacity: 1; }
|
|
||||||
100% { opacity: 0; pointer-events: none; }
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -58,12 +58,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ButtonMoreItem } from '@/components/core/forms/art-button-more/index.vue'
|
import { ButtonMoreItem } from '@/components/core/forms/art-button-more/index.vue'
|
||||||
import { useTable } from '@/hooks/core/useTable'
|
import { useTable } from '@/hooks/core/useTable'
|
||||||
import { fetchGetRoleList, fetchDeleteRole } from '@/api/system-manage'
|
import { fetchGetRoleList } from '@/api/system-manage'
|
||||||
import ArtButtonMore from '@/components/core/forms/art-button-more/index.vue'
|
import ArtButtonMore from '@/components/core/forms/art-button-more/index.vue'
|
||||||
import RoleSearch from './modules/role-search.vue'
|
import RoleSearch from './modules/role-search.vue'
|
||||||
import RoleEditDialog from './modules/role-edit-dialog.vue'
|
import RoleEditDialog from './modules/role-edit-dialog.vue'
|
||||||
import RolePermissionDialog from './modules/role-permission-dialog.vue'
|
import RolePermissionDialog from './modules/role-permission-dialog.vue'
|
||||||
import { ElTag, ElMessageBox, ElMessage } from 'element-plus'
|
import { ElTag, ElMessageBox } from 'element-plus'
|
||||||
|
|
||||||
defineOptions({ name: 'Role' })
|
defineOptions({ name: 'Role' })
|
||||||
|
|
||||||
@ -224,20 +224,19 @@
|
|||||||
currentRoleData.value = row
|
currentRoleData.value = row
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteRole = async (row: RoleListItem) => {
|
const deleteRole = (row: RoleListItem) => {
|
||||||
try {
|
ElMessageBox.confirm(`确定删除角色"${row.roleName}"吗?此操作不可恢复!`, '删除确认', {
|
||||||
await ElMessageBox.confirm(`确定删除角色"${row.roleName}"吗?此操作不可恢复!`, '删除确认', {
|
confirmButtonText: '确定',
|
||||||
confirmButtonText: '确定',
|
cancelButtonText: '取消',
|
||||||
cancelButtonText: '取消',
|
type: 'warning'
|
||||||
type: 'warning'
|
})
|
||||||
|
.then(() => {
|
||||||
|
// TODO: 调用删除接口
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
refreshData()
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
ElMessage.info('已取消删除')
|
||||||
})
|
})
|
||||||
|
|
||||||
await fetchDeleteRole(row.roleId)
|
|
||||||
refreshData()
|
|
||||||
} catch (error: any) {
|
|
||||||
if (error !== 'cancel') {
|
|
||||||
console.error('删除角色失败:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -27,14 +27,13 @@
|
|||||||
</ElForm>
|
</ElForm>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<ElButton @click="handleClose">取消</ElButton>
|
<ElButton @click="handleClose">取消</ElButton>
|
||||||
<ElButton type="primary" :loading="submitting" @click="handleSubmit">提交</ElButton>
|
<ElButton type="primary" @click="handleSubmit">提交</ElButton>
|
||||||
</template>
|
</template>
|
||||||
</ElDialog>
|
</ElDialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
import { fetchCreateRole, fetchUpdateRole } from '@/api/system-manage'
|
|
||||||
|
|
||||||
type RoleListItem = Api.SystemManage.RoleListItem
|
type RoleListItem = Api.SystemManage.RoleListItem
|
||||||
|
|
||||||
@ -58,7 +57,6 @@
|
|||||||
const emit = defineEmits<Emits>()
|
const emit = defineEmits<Emits>()
|
||||||
|
|
||||||
const formRef = ref<FormInstance>()
|
const formRef = ref<FormInstance>()
|
||||||
const submitting = ref(false)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 弹窗显示状态双向绑定
|
* 弹窗显示状态双向绑定
|
||||||
@ -79,7 +77,8 @@
|
|||||||
roleCode: [
|
roleCode: [
|
||||||
{ required: true, message: '请输入角色编码', trigger: 'blur' },
|
{ required: true, message: '请输入角色编码', trigger: 'blur' },
|
||||||
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
|
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
|
||||||
]
|
],
|
||||||
|
description: [{ required: true, message: '请输入角色描述', trigger: 'blur' }]
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -151,30 +150,13 @@
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await formRef.value.validate()
|
await formRef.value.validate()
|
||||||
submitting.value = true
|
// TODO: 调用新增/编辑接口
|
||||||
|
const message = props.dialogType === 'add' ? '新增成功' : '修改成功'
|
||||||
if (props.dialogType === 'add') {
|
ElMessage.success(message)
|
||||||
await fetchCreateRole({
|
|
||||||
roleName: form.roleName,
|
|
||||||
roleCode: form.roleCode,
|
|
||||||
description: form.description || undefined,
|
|
||||||
enabled: form.enabled
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
await fetchUpdateRole(form.roleId, {
|
|
||||||
roleName: form.roleName,
|
|
||||||
roleCode: form.roleCode,
|
|
||||||
description: form.description || undefined,
|
|
||||||
enabled: form.enabled
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
emit('success')
|
emit('success')
|
||||||
handleClose()
|
handleClose()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('表单验证失败:', error)
|
console.log('表单验证失败:', error)
|
||||||
} finally {
|
|
||||||
submitting.value = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -12,9 +12,9 @@
|
|||||||
ref="treeRef"
|
ref="treeRef"
|
||||||
:data="processedMenuList"
|
:data="processedMenuList"
|
||||||
show-checkbox
|
show-checkbox
|
||||||
node-key="id"
|
node-key="name"
|
||||||
:default-expand-all="isExpandAll"
|
:default-expand-all="isExpandAll"
|
||||||
:default-checked-keys="checkedMenuIds"
|
:default-checked-keys="[1, 2, 3]"
|
||||||
:props="defaultProps"
|
:props="defaultProps"
|
||||||
@check="handleTreeCheck"
|
@check="handleTreeCheck"
|
||||||
>
|
>
|
||||||
@ -29,11 +29,13 @@
|
|||||||
</ElTree>
|
</ElTree>
|
||||||
</ElScrollbar>
|
</ElScrollbar>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
<ElButton @click="outputSelectedData" style="margin-left: 8px">获取选中数据</ElButton>
|
||||||
|
|
||||||
<ElButton @click="toggleExpandAll">{{ isExpandAll ? '全部收起' : '全部展开' }}</ElButton>
|
<ElButton @click="toggleExpandAll">{{ isExpandAll ? '全部收起' : '全部展开' }}</ElButton>
|
||||||
<ElButton @click="toggleSelectAll" style="margin-left: 8px">{{
|
<ElButton @click="toggleSelectAll" style="margin-left: 8px">{{
|
||||||
isSelectAll ? '取消全选' : '全部选择'
|
isSelectAll ? '取消全选' : '全部选择'
|
||||||
}}</ElButton>
|
}}</ElButton>
|
||||||
<ElButton type="primary" :loading="saving" @click="savePermission">保存</ElButton>
|
<ElButton type="primary" @click="savePermission">保存</ElButton>
|
||||||
</template>
|
</template>
|
||||||
</ElDialog>
|
</ElDialog>
|
||||||
</template>
|
</template>
|
||||||
@ -41,7 +43,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useMenuStore } from '@/store/modules/menu'
|
import { useMenuStore } from '@/store/modules/menu'
|
||||||
import { formatMenuTitle } from '@/utils/router'
|
import { formatMenuTitle } from '@/utils/router'
|
||||||
import { fetchGetRoleMenus, fetchSetRoleMenus } from '@/api/system-manage'
|
|
||||||
|
|
||||||
type RoleListItem = Api.SystemManage.RoleListItem
|
type RoleListItem = Api.SystemManage.RoleListItem
|
||||||
|
|
||||||
@ -66,8 +67,6 @@
|
|||||||
const treeRef = ref()
|
const treeRef = ref()
|
||||||
const isExpandAll = ref(true)
|
const isExpandAll = ref(true)
|
||||||
const isSelectAll = ref(false)
|
const isSelectAll = ref(false)
|
||||||
const checkedMenuIds = ref<number[]>([])
|
|
||||||
const saving = ref(false)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 弹窗显示状态双向绑定
|
* 弹窗显示状态双向绑定
|
||||||
@ -142,19 +141,10 @@
|
|||||||
*/
|
*/
|
||||||
watch(
|
watch(
|
||||||
() => props.modelValue,
|
() => props.modelValue,
|
||||||
async (newVal) => {
|
(newVal) => {
|
||||||
if (newVal && props.roleData) {
|
if (newVal && props.roleData) {
|
||||||
try {
|
// TODO: 根据角色加载对应的权限数据
|
||||||
const menuIds = await fetchGetRoleMenus(props.roleData.roleId)
|
console.log('设置权限:', props.roleData)
|
||||||
checkedMenuIds.value = menuIds || []
|
|
||||||
// 等待 DOM 更新后设置选中状态
|
|
||||||
nextTick(() => {
|
|
||||||
treeRef.value?.setCheckedKeys(checkedMenuIds.value)
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取角色菜单权限失败:', error)
|
|
||||||
checkedMenuIds.value = []
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -164,35 +154,17 @@
|
|||||||
*/
|
*/
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
visible.value = false
|
visible.value = false
|
||||||
checkedMenuIds.value = []
|
|
||||||
treeRef.value?.setCheckedKeys([])
|
treeRef.value?.setCheckedKeys([])
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存权限配置
|
* 保存权限配置
|
||||||
*/
|
*/
|
||||||
const savePermission = async () => {
|
const savePermission = () => {
|
||||||
if (!props.roleData) return
|
// TODO: 调用保存权限接口
|
||||||
|
ElMessage.success('权限保存成功')
|
||||||
const tree = treeRef.value
|
emit('success')
|
||||||
if (!tree) return
|
handleClose()
|
||||||
|
|
||||||
// 获取选中的菜单ID(只获取数字类型的ID,排除权限节点)
|
|
||||||
const checkedKeys = tree.getCheckedKeys()
|
|
||||||
const halfCheckedKeys = tree.getHalfCheckedKeys()
|
|
||||||
const allKeys = [...checkedKeys, ...halfCheckedKeys]
|
|
||||||
const menuIds = allKeys.filter((key: any) => typeof key === 'number') as number[]
|
|
||||||
|
|
||||||
saving.value = true
|
|
||||||
try {
|
|
||||||
await fetchSetRoleMenus(props.roleData.roleId, menuIds)
|
|
||||||
emit('success')
|
|
||||||
handleClose()
|
|
||||||
} catch (error) {
|
|
||||||
console.error('保存权限失败:', error)
|
|
||||||
} finally {
|
|
||||||
saving.value = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -233,11 +205,11 @@
|
|||||||
* @param nodes 节点列表
|
* @param nodes 节点列表
|
||||||
* @returns 所有节点的 key 数组
|
* @returns 所有节点的 key 数组
|
||||||
*/
|
*/
|
||||||
const getAllNodeKeys = (nodes: MenuNode[]): (string | number)[] => {
|
const getAllNodeKeys = (nodes: MenuNode[]): string[] => {
|
||||||
const keys: (string | number)[] = []
|
const keys: string[] = []
|
||||||
const traverse = (nodeList: MenuNode[]): void => {
|
const traverse = (nodeList: MenuNode[]): void => {
|
||||||
nodeList.forEach((node) => {
|
nodeList.forEach((node) => {
|
||||||
if (node.id !== undefined) keys.push(node.id)
|
if (node.name) keys.push(node.name)
|
||||||
if (node.children?.length) traverse(node.children)
|
if (node.children?.length) traverse(node.children)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -258,4 +230,25 @@
|
|||||||
|
|
||||||
isSelectAll.value = checkedKeys.length === allKeys.length && allKeys.length > 0
|
isSelectAll.value = checkedKeys.length === allKeys.length && allKeys.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出选中的权限数据到控制台
|
||||||
|
* 用于调试和查看当前选中的权限配置
|
||||||
|
*/
|
||||||
|
const outputSelectedData = () => {
|
||||||
|
const tree = treeRef.value
|
||||||
|
if (!tree) return
|
||||||
|
|
||||||
|
const selectedData = {
|
||||||
|
checkedKeys: tree.getCheckedKeys(),
|
||||||
|
halfCheckedKeys: tree.getHalfCheckedKeys(),
|
||||||
|
checkedNodes: tree.getCheckedNodes(),
|
||||||
|
halfCheckedNodes: tree.getHalfCheckedNodes(),
|
||||||
|
totalChecked: tree.getCheckedKeys().length,
|
||||||
|
totalHalfChecked: tree.getHalfCheckedKeys().length
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('=== 选中的权限数据 ===', selectedData)
|
||||||
|
ElMessage.success(`已输出选中数据到控制台,共选中 ${selectedData.totalChecked} 个节点`)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -16,7 +16,6 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||||
<PackageReference Include="System.Management" Version="8.0.0" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -1,323 +0,0 @@
|
|||||||
using AmtScanner.Api.Data;
|
|
||||||
using AmtScanner.Api.Models;
|
|
||||||
using AmtScanner.Api.Services;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
namespace AmtScanner.Api.Controllers;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/os-devices")]
|
|
||||||
public class OsDevicesController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly AppDbContext _context;
|
|
||||||
private readonly IWindowsScannerService _scannerService;
|
|
||||||
private readonly ILogger<OsDevicesController> _logger;
|
|
||||||
private static readonly ConcurrentDictionary<string, OsScanProgress> _scanProgress = new();
|
|
||||||
|
|
||||||
public OsDevicesController(
|
|
||||||
AppDbContext context,
|
|
||||||
IWindowsScannerService scannerService,
|
|
||||||
ILogger<OsDevicesController> logger)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
_scannerService = scannerService;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取所有操作系统设备
|
|
||||||
/// </summary>
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<ActionResult<ApiResponse<List<OsDeviceDto>>>> GetAll()
|
|
||||||
{
|
|
||||||
var devices = await _context.OsDevices
|
|
||||||
.Include(o => o.AmtDevice)
|
|
||||||
.OrderByDescending(o => o.LastUpdatedAt)
|
|
||||||
.Select(o => new OsDeviceDto
|
|
||||||
{
|
|
||||||
Id = o.Id,
|
|
||||||
IpAddress = o.IpAddress,
|
|
||||||
SystemUuid = o.SystemUuid,
|
|
||||||
Hostname = o.Hostname,
|
|
||||||
OsType = o.OsType.ToString(),
|
|
||||||
OsVersion = o.OsVersion,
|
|
||||||
Architecture = o.Architecture,
|
|
||||||
LoggedInUser = o.LoggedInUser,
|
|
||||||
LastBootTime = o.LastBootTime,
|
|
||||||
MacAddress = o.MacAddress,
|
|
||||||
IsOnline = o.IsOnline,
|
|
||||||
LastOnlineAt = o.LastOnlineAt,
|
|
||||||
DiscoveredAt = o.DiscoveredAt,
|
|
||||||
LastUpdatedAt = o.LastUpdatedAt,
|
|
||||||
Description = o.Description,
|
|
||||||
AmtDeviceId = o.AmtDeviceId,
|
|
||||||
AmtDeviceIp = o.AmtDevice != null ? o.AmtDevice.IpAddress : null
|
|
||||||
})
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
return Ok(ApiResponse<List<OsDeviceDto>>.Success(devices));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取单个操作系统设备
|
|
||||||
/// </summary>
|
|
||||||
[HttpGet("{id}")]
|
|
||||||
public async Task<ActionResult<ApiResponse<OsDeviceDto>>> GetById(long id)
|
|
||||||
{
|
|
||||||
var device = await _context.OsDevices
|
|
||||||
.Include(o => o.AmtDevice)
|
|
||||||
.FirstOrDefaultAsync(o => o.Id == id);
|
|
||||||
|
|
||||||
if (device == null)
|
|
||||||
return Ok(ApiResponse<OsDeviceDto>.Fail(404, "设备不存在"));
|
|
||||||
|
|
||||||
return Ok(ApiResponse<OsDeviceDto>.Success(new OsDeviceDto
|
|
||||||
{
|
|
||||||
Id = device.Id,
|
|
||||||
IpAddress = device.IpAddress,
|
|
||||||
SystemUuid = device.SystemUuid,
|
|
||||||
Hostname = device.Hostname,
|
|
||||||
OsType = device.OsType.ToString(),
|
|
||||||
OsVersion = device.OsVersion,
|
|
||||||
Architecture = device.Architecture,
|
|
||||||
LoggedInUser = device.LoggedInUser,
|
|
||||||
LastBootTime = device.LastBootTime,
|
|
||||||
MacAddress = device.MacAddress,
|
|
||||||
IsOnline = device.IsOnline,
|
|
||||||
LastOnlineAt = device.LastOnlineAt,
|
|
||||||
DiscoveredAt = device.DiscoveredAt,
|
|
||||||
LastUpdatedAt = device.LastUpdatedAt,
|
|
||||||
Description = device.Description,
|
|
||||||
AmtDeviceId = device.AmtDeviceId,
|
|
||||||
AmtDeviceIp = device.AmtDevice?.IpAddress
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 启动操作系统扫描
|
|
||||||
/// </summary>
|
|
||||||
[HttpPost("scan/start")]
|
|
||||||
public async Task<ActionResult<ApiResponse<OsScanStartResponse>>> StartScan(
|
|
||||||
[FromBody] OsScanRequest request)
|
|
||||||
{
|
|
||||||
var taskId = Guid.NewGuid().ToString("N");
|
|
||||||
|
|
||||||
var progress = new Progress<OsScanProgress>(p =>
|
|
||||||
{
|
|
||||||
_scanProgress[taskId] = p;
|
|
||||||
});
|
|
||||||
|
|
||||||
_ = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await _scannerService.ScanNetworkAsync(taskId, request.NetworkSegment, request.SubnetMask, progress);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "OS scan failed for task {TaskId}", taskId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return Ok(ApiResponse<OsScanStartResponse>.Success(new OsScanStartResponse
|
|
||||||
{
|
|
||||||
TaskId = taskId,
|
|
||||||
Message = "操作系统扫描已启动"
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取扫描进度
|
|
||||||
/// </summary>
|
|
||||||
[HttpGet("scan/status/{taskId}")]
|
|
||||||
public ActionResult<ApiResponse<OsScanProgress>> GetScanStatus(string taskId)
|
|
||||||
{
|
|
||||||
if (_scanProgress.TryGetValue(taskId, out var progress))
|
|
||||||
{
|
|
||||||
return Ok(ApiResponse<OsScanProgress>.Success(progress));
|
|
||||||
}
|
|
||||||
return Ok(ApiResponse<OsScanProgress>.Fail(404, "扫描任务不存在"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 取消扫描
|
|
||||||
/// </summary>
|
|
||||||
[HttpPost("scan/cancel/{taskId}")]
|
|
||||||
public ActionResult<ApiResponse<object>> CancelScan(string taskId)
|
|
||||||
{
|
|
||||||
_scannerService.CancelScan(taskId);
|
|
||||||
return Ok(ApiResponse<object>.Success(null, "扫描已取消"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取设备详细信息(通过 WMI)
|
|
||||||
/// </summary>
|
|
||||||
[HttpPost("{id}/fetch-info")]
|
|
||||||
public async Task<ActionResult<ApiResponse<OsDeviceDto>>> FetchDeviceInfo(
|
|
||||||
long id,
|
|
||||||
[FromBody] WmiCredentials credentials)
|
|
||||||
{
|
|
||||||
var device = await _context.OsDevices.FindAsync(id);
|
|
||||||
if (device == null)
|
|
||||||
return Ok(ApiResponse<OsDeviceDto>.Fail(404, "设备不存在"));
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var osInfo = await _scannerService.GetOsInfoAsync(
|
|
||||||
device.IpAddress,
|
|
||||||
credentials.Username,
|
|
||||||
credentials.Password);
|
|
||||||
|
|
||||||
if (osInfo == null)
|
|
||||||
return Ok(ApiResponse<OsDeviceDto>.Fail(500, "无法获取系统信息。可能原因:1) 目标机器WMI服务未启动 2) 防火墙阻止连接 3) 凭据不正确 4) 目标机器不允许远程WMI连接"));
|
|
||||||
|
|
||||||
// 更新设备信息
|
|
||||||
device.SystemUuid = osInfo.SystemUuid;
|
|
||||||
device.Hostname = osInfo.Hostname;
|
|
||||||
device.OsVersion = osInfo.OsVersion;
|
|
||||||
device.Architecture = osInfo.Architecture;
|
|
||||||
device.LoggedInUser = osInfo.LoggedInUser;
|
|
||||||
device.LastBootTime = osInfo.LastBootTime;
|
|
||||||
device.MacAddress = osInfo.MacAddress;
|
|
||||||
device.LastUpdatedAt = DateTime.UtcNow;
|
|
||||||
device.Description = "通过 WMI 获取详细信息";
|
|
||||||
|
|
||||||
await _context.SaveChangesAsync();
|
|
||||||
|
|
||||||
// 尝试绑定 AMT 设备
|
|
||||||
await _scannerService.BindAmtDevicesAsync();
|
|
||||||
|
|
||||||
// 重新加载以获取关联的 AMT 设备
|
|
||||||
await _context.Entry(device).Reference(d => d.AmtDevice).LoadAsync();
|
|
||||||
|
|
||||||
return Ok(ApiResponse<OsDeviceDto>.Success(new OsDeviceDto
|
|
||||||
{
|
|
||||||
Id = device.Id,
|
|
||||||
IpAddress = device.IpAddress,
|
|
||||||
SystemUuid = device.SystemUuid,
|
|
||||||
Hostname = device.Hostname,
|
|
||||||
OsType = device.OsType.ToString(),
|
|
||||||
OsVersion = device.OsVersion,
|
|
||||||
Architecture = device.Architecture,
|
|
||||||
LoggedInUser = device.LoggedInUser,
|
|
||||||
LastBootTime = device.LastBootTime,
|
|
||||||
MacAddress = device.MacAddress,
|
|
||||||
IsOnline = device.IsOnline,
|
|
||||||
LastOnlineAt = device.LastOnlineAt,
|
|
||||||
DiscoveredAt = device.DiscoveredAt,
|
|
||||||
LastUpdatedAt = device.LastUpdatedAt,
|
|
||||||
Description = device.Description,
|
|
||||||
AmtDeviceId = device.AmtDeviceId,
|
|
||||||
AmtDeviceIp = device.AmtDevice?.IpAddress
|
|
||||||
}, "系统信息已更新"));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "获取设备 {Id} 系统信息失败", id);
|
|
||||||
return Ok(ApiResponse<OsDeviceDto>.Fail(500, $"获取系统信息失败: {ex.Message}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 手动绑定 AMT 设备
|
|
||||||
/// </summary>
|
|
||||||
[HttpPost("{id}/bind-amt/{amtDeviceId}")]
|
|
||||||
public async Task<ActionResult<ApiResponse<object>>> BindAmtDevice(long id, long amtDeviceId)
|
|
||||||
{
|
|
||||||
var osDevice = await _context.OsDevices.FindAsync(id);
|
|
||||||
if (osDevice == null)
|
|
||||||
return Ok(ApiResponse<object>.Fail(404, "操作系统设备不存在"));
|
|
||||||
|
|
||||||
var amtDevice = await _context.AmtDevices.FindAsync(amtDeviceId);
|
|
||||||
if (amtDevice == null)
|
|
||||||
return Ok(ApiResponse<object>.Fail(404, "AMT 设备不存在"));
|
|
||||||
|
|
||||||
osDevice.AmtDeviceId = amtDeviceId;
|
|
||||||
await _context.SaveChangesAsync();
|
|
||||||
|
|
||||||
return Ok(ApiResponse<object>.Success(null, "绑定成功"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 解除 AMT 绑定
|
|
||||||
/// </summary>
|
|
||||||
[HttpPost("{id}/unbind-amt")]
|
|
||||||
public async Task<ActionResult<ApiResponse<object>>> UnbindAmtDevice(long id)
|
|
||||||
{
|
|
||||||
var osDevice = await _context.OsDevices.FindAsync(id);
|
|
||||||
if (osDevice == null)
|
|
||||||
return Ok(ApiResponse<object>.Fail(404, "设备不存在"));
|
|
||||||
|
|
||||||
osDevice.AmtDeviceId = null;
|
|
||||||
await _context.SaveChangesAsync();
|
|
||||||
|
|
||||||
return Ok(ApiResponse<object>.Success(null, "已解除绑定"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自动绑定所有设备
|
|
||||||
/// </summary>
|
|
||||||
[HttpPost("auto-bind")]
|
|
||||||
public async Task<ActionResult<ApiResponse<object>>> AutoBindAll()
|
|
||||||
{
|
|
||||||
await _scannerService.BindAmtDevicesAsync();
|
|
||||||
return Ok(ApiResponse<object>.Success(null, "自动绑定完成"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 删除设备
|
|
||||||
/// </summary>
|
|
||||||
[HttpDelete("{id}")]
|
|
||||||
public async Task<ActionResult<ApiResponse<object>>> Delete(long id)
|
|
||||||
{
|
|
||||||
var device = await _context.OsDevices.FindAsync(id);
|
|
||||||
if (device == null)
|
|
||||||
return Ok(ApiResponse<object>.Fail(404, "设备不存在"));
|
|
||||||
|
|
||||||
_context.OsDevices.Remove(device);
|
|
||||||
await _context.SaveChangesAsync();
|
|
||||||
|
|
||||||
return Ok(ApiResponse<object>.Success(null, "删除成功"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class OsDeviceDto
|
|
||||||
{
|
|
||||||
public long Id { get; set; }
|
|
||||||
public string IpAddress { get; set; } = string.Empty;
|
|
||||||
public string? SystemUuid { get; set; }
|
|
||||||
public string? Hostname { get; set; }
|
|
||||||
public string? OsType { get; set; }
|
|
||||||
public string? OsVersion { get; set; }
|
|
||||||
public string? Architecture { get; set; }
|
|
||||||
public string? LoggedInUser { get; set; }
|
|
||||||
public DateTime? LastBootTime { get; set; }
|
|
||||||
public string? MacAddress { get; set; }
|
|
||||||
public bool IsOnline { get; set; }
|
|
||||||
public DateTime? LastOnlineAt { get; set; }
|
|
||||||
public DateTime DiscoveredAt { get; set; }
|
|
||||||
public DateTime LastUpdatedAt { get; set; }
|
|
||||||
public string? Description { get; set; }
|
|
||||||
public long? AmtDeviceId { get; set; }
|
|
||||||
public string? AmtDeviceIp { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class WmiCredentials
|
|
||||||
{
|
|
||||||
public string Username { get; set; } = string.Empty;
|
|
||||||
public string Password { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class OsScanStartResponse
|
|
||||||
{
|
|
||||||
public string TaskId { get; set; } = string.Empty;
|
|
||||||
public string Message { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class OsScanRequest
|
|
||||||
{
|
|
||||||
public string NetworkSegment { get; set; } = string.Empty;
|
|
||||||
public string SubnetMask { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
@ -67,231 +67,6 @@ public class RoleController : ControllerBase
|
|||||||
Total = total
|
Total = total
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取所有角色(下拉选择用)
|
|
||||||
/// </summary>
|
|
||||||
[HttpGet("all")]
|
|
||||||
public async Task<ActionResult<ApiResponse<List<RoleSimpleDto>>>> GetAllRoles()
|
|
||||||
{
|
|
||||||
var roles = await _context.Roles
|
|
||||||
.Where(r => r.Enabled)
|
|
||||||
.OrderBy(r => r.Id)
|
|
||||||
.Select(r => new RoleSimpleDto
|
|
||||||
{
|
|
||||||
RoleId = r.Id,
|
|
||||||
RoleName = r.RoleName,
|
|
||||||
RoleCode = r.RoleCode
|
|
||||||
})
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
return Ok(ApiResponse<List<RoleSimpleDto>>.Success(roles));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取角色详情
|
|
||||||
/// </summary>
|
|
||||||
[HttpGet("{id}")]
|
|
||||||
public async Task<ActionResult<ApiResponse<RoleListItemDto>>> GetRole(int id)
|
|
||||||
{
|
|
||||||
var role = await _context.Roles.FindAsync(id);
|
|
||||||
if (role == null)
|
|
||||||
{
|
|
||||||
return NotFound(ApiResponse<RoleListItemDto>.Fail(404, "角色不存在"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(ApiResponse<RoleListItemDto>.Success(new RoleListItemDto
|
|
||||||
{
|
|
||||||
RoleId = role.Id,
|
|
||||||
RoleName = role.RoleName,
|
|
||||||
RoleCode = role.RoleCode,
|
|
||||||
Description = role.Description,
|
|
||||||
Enabled = role.Enabled,
|
|
||||||
CreateTime = role.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss")
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 创建角色
|
|
||||||
/// </summary>
|
|
||||||
[HttpPost]
|
|
||||||
public async Task<ActionResult<ApiResponse<RoleListItemDto>>> CreateRole([FromBody] CreateRoleDto dto)
|
|
||||||
{
|
|
||||||
// 检查角色编码是否已存在
|
|
||||||
if (await _context.Roles.AnyAsync(r => r.RoleCode == dto.RoleCode))
|
|
||||||
{
|
|
||||||
return BadRequest(ApiResponse<RoleListItemDto>.Fail(400, "角色编码已存在"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查角色名称是否已存在
|
|
||||||
if (await _context.Roles.AnyAsync(r => r.RoleName == dto.RoleName))
|
|
||||||
{
|
|
||||||
return BadRequest(ApiResponse<RoleListItemDto>.Fail(400, "角色名称已存在"));
|
|
||||||
}
|
|
||||||
|
|
||||||
var role = new Role
|
|
||||||
{
|
|
||||||
RoleName = dto.RoleName,
|
|
||||||
RoleCode = dto.RoleCode,
|
|
||||||
Description = dto.Description,
|
|
||||||
Enabled = dto.Enabled,
|
|
||||||
CreatedAt = DateTime.UtcNow
|
|
||||||
};
|
|
||||||
|
|
||||||
_context.Roles.Add(role);
|
|
||||||
await _context.SaveChangesAsync();
|
|
||||||
|
|
||||||
return Ok(ApiResponse<RoleListItemDto>.Success(new RoleListItemDto
|
|
||||||
{
|
|
||||||
RoleId = role.Id,
|
|
||||||
RoleName = role.RoleName,
|
|
||||||
RoleCode = role.RoleCode,
|
|
||||||
Description = role.Description,
|
|
||||||
Enabled = role.Enabled,
|
|
||||||
CreateTime = role.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss")
|
|
||||||
}, "创建成功"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 更新角色
|
|
||||||
/// </summary>
|
|
||||||
[HttpPut("{id}")]
|
|
||||||
public async Task<ActionResult<ApiResponse<RoleListItemDto>>> UpdateRole(int id, [FromBody] UpdateRoleDto dto)
|
|
||||||
{
|
|
||||||
var role = await _context.Roles.FindAsync(id);
|
|
||||||
if (role == null)
|
|
||||||
{
|
|
||||||
return NotFound(ApiResponse<RoleListItemDto>.Fail(404, "角色不存在"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查角色编码是否与其他角色重复
|
|
||||||
if (!string.IsNullOrEmpty(dto.RoleCode) && dto.RoleCode != role.RoleCode)
|
|
||||||
{
|
|
||||||
if (await _context.Roles.AnyAsync(r => r.RoleCode == dto.RoleCode && r.Id != id))
|
|
||||||
{
|
|
||||||
return BadRequest(ApiResponse<RoleListItemDto>.Fail(400, "角色编码已存在"));
|
|
||||||
}
|
|
||||||
role.RoleCode = dto.RoleCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查角色名称是否与其他角色重复
|
|
||||||
if (!string.IsNullOrEmpty(dto.RoleName) && dto.RoleName != role.RoleName)
|
|
||||||
{
|
|
||||||
if (await _context.Roles.AnyAsync(r => r.RoleName == dto.RoleName && r.Id != id))
|
|
||||||
{
|
|
||||||
return BadRequest(ApiResponse<RoleListItemDto>.Fail(400, "角色名称已存在"));
|
|
||||||
}
|
|
||||||
role.RoleName = dto.RoleName;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dto.Description != null)
|
|
||||||
{
|
|
||||||
role.Description = dto.Description;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dto.Enabled.HasValue)
|
|
||||||
{
|
|
||||||
role.Enabled = dto.Enabled.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
await _context.SaveChangesAsync();
|
|
||||||
|
|
||||||
return Ok(ApiResponse<RoleListItemDto>.Success(new RoleListItemDto
|
|
||||||
{
|
|
||||||
RoleId = role.Id,
|
|
||||||
RoleName = role.RoleName,
|
|
||||||
RoleCode = role.RoleCode,
|
|
||||||
Description = role.Description,
|
|
||||||
Enabled = role.Enabled,
|
|
||||||
CreateTime = role.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss")
|
|
||||||
}, "更新成功"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 删除角色
|
|
||||||
/// </summary>
|
|
||||||
[HttpDelete("{id}")]
|
|
||||||
public async Task<ActionResult<ApiResponse<object>>> DeleteRole(int id)
|
|
||||||
{
|
|
||||||
var role = await _context.Roles
|
|
||||||
.Include(r => r.UserRoles)
|
|
||||||
.Include(r => r.RoleMenus)
|
|
||||||
.FirstOrDefaultAsync(r => r.Id == id);
|
|
||||||
|
|
||||||
if (role == null)
|
|
||||||
{
|
|
||||||
return NotFound(ApiResponse<object>.Fail(404, "角色不存在"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否有用户使用该角色
|
|
||||||
if (role.UserRoles.Any())
|
|
||||||
{
|
|
||||||
return BadRequest(ApiResponse<object>.Fail(400, "该角色下存在用户,无法删除"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除角色菜单关联
|
|
||||||
_context.RoleMenus.RemoveRange(role.RoleMenus);
|
|
||||||
|
|
||||||
// 删除角色
|
|
||||||
_context.Roles.Remove(role);
|
|
||||||
await _context.SaveChangesAsync();
|
|
||||||
|
|
||||||
return Ok(ApiResponse<object>.Success(null, "删除成功"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取角色的菜单权限
|
|
||||||
/// </summary>
|
|
||||||
[HttpGet("{id}/menus")]
|
|
||||||
public async Task<ActionResult<ApiResponse<List<int>>>> GetRoleMenus(int id)
|
|
||||||
{
|
|
||||||
var role = await _context.Roles.FindAsync(id);
|
|
||||||
if (role == null)
|
|
||||||
{
|
|
||||||
return NotFound(ApiResponse<List<int>>.Fail(404, "角色不存在"));
|
|
||||||
}
|
|
||||||
|
|
||||||
var menuIds = await _context.RoleMenus
|
|
||||||
.Where(rm => rm.RoleId == id)
|
|
||||||
.Select(rm => rm.MenuId)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
return Ok(ApiResponse<List<int>>.Success(menuIds));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设置角色的菜单权限
|
|
||||||
/// </summary>
|
|
||||||
[HttpPut("{id}/menus")]
|
|
||||||
public async Task<ActionResult<ApiResponse<object>>> SetRoleMenus(int id, [FromBody] SetRoleMenusDto dto)
|
|
||||||
{
|
|
||||||
var role = await _context.Roles.FindAsync(id);
|
|
||||||
if (role == null)
|
|
||||||
{
|
|
||||||
return NotFound(ApiResponse<object>.Fail(404, "角色不存在"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除原有的菜单权限
|
|
||||||
var existingMenus = await _context.RoleMenus
|
|
||||||
.Where(rm => rm.RoleId == id)
|
|
||||||
.ToListAsync();
|
|
||||||
_context.RoleMenus.RemoveRange(existingMenus);
|
|
||||||
|
|
||||||
// 添加新的菜单权限
|
|
||||||
if (dto.MenuIds != null && dto.MenuIds.Any())
|
|
||||||
{
|
|
||||||
var newMenus = dto.MenuIds.Select(menuId => new RoleMenu
|
|
||||||
{
|
|
||||||
RoleId = id,
|
|
||||||
MenuId = menuId
|
|
||||||
});
|
|
||||||
_context.RoleMenus.AddRange(newMenus);
|
|
||||||
}
|
|
||||||
|
|
||||||
await _context.SaveChangesAsync();
|
|
||||||
|
|
||||||
return Ok(ApiResponse<object>.Success(null, "菜单权限设置成功"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -306,43 +81,3 @@ public class RoleListItemDto
|
|||||||
public bool Enabled { get; set; }
|
public bool Enabled { get; set; }
|
||||||
public string CreateTime { get; set; } = string.Empty;
|
public string CreateTime { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 角色简单 DTO(下拉选择用)
|
|
||||||
/// </summary>
|
|
||||||
public class RoleSimpleDto
|
|
||||||
{
|
|
||||||
public int RoleId { get; set; }
|
|
||||||
public string RoleName { get; set; } = string.Empty;
|
|
||||||
public string RoleCode { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 创建角色 DTO
|
|
||||||
/// </summary>
|
|
||||||
public class CreateRoleDto
|
|
||||||
{
|
|
||||||
public string RoleName { get; set; } = string.Empty;
|
|
||||||
public string RoleCode { get; set; } = string.Empty;
|
|
||||||
public string? Description { get; set; }
|
|
||||||
public bool Enabled { get; set; } = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 更新角色 DTO
|
|
||||||
/// </summary>
|
|
||||||
public class UpdateRoleDto
|
|
||||||
{
|
|
||||||
public string? RoleName { get; set; }
|
|
||||||
public string? RoleCode { get; set; }
|
|
||||||
public string? Description { get; set; }
|
|
||||||
public bool? Enabled { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设置角色菜单 DTO
|
|
||||||
/// </summary>
|
|
||||||
public class SetRoleMenusDto
|
|
||||||
{
|
|
||||||
public List<int>? MenuIds { get; set; }
|
|
||||||
}
|
|
||||||
|
|||||||
@ -17,7 +17,6 @@ public class AppDbContext : DbContext
|
|||||||
public DbSet<StorageDevice> StorageDevices { get; set; }
|
public DbSet<StorageDevice> StorageDevices { get; set; }
|
||||||
public DbSet<WindowsCredential> WindowsCredentials { get; set; }
|
public DbSet<WindowsCredential> WindowsCredentials { get; set; }
|
||||||
public DbSet<RemoteAccessToken> RemoteAccessTokens { get; set; }
|
public DbSet<RemoteAccessToken> RemoteAccessTokens { get; set; }
|
||||||
public DbSet<OsDevice> OsDevices { get; set; }
|
|
||||||
|
|
||||||
// 用户认证相关
|
// 用户认证相关
|
||||||
public DbSet<User> Users { get; set; }
|
public DbSet<User> Users { get; set; }
|
||||||
@ -162,35 +161,5 @@ public class AppDbContext : DbContext
|
|||||||
.WithMany(m => m.RoleMenus)
|
.WithMany(m => m.RoleMenus)
|
||||||
.HasForeignKey(rm => rm.MenuId)
|
.HasForeignKey(rm => rm.MenuId)
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
// OsDevice 配置
|
|
||||||
modelBuilder.Entity<OsDevice>()
|
|
||||||
.Property(o => o.IpAddress)
|
|
||||||
.HasMaxLength(50);
|
|
||||||
|
|
||||||
modelBuilder.Entity<OsDevice>()
|
|
||||||
.HasIndex(o => o.IpAddress)
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
modelBuilder.Entity<OsDevice>()
|
|
||||||
.Property(o => o.SystemUuid)
|
|
||||||
.HasMaxLength(50);
|
|
||||||
|
|
||||||
modelBuilder.Entity<OsDevice>()
|
|
||||||
.HasIndex(o => o.SystemUuid);
|
|
||||||
|
|
||||||
modelBuilder.Entity<OsDevice>()
|
|
||||||
.HasOne(o => o.AmtDevice)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(o => o.AmtDeviceId)
|
|
||||||
.OnDelete(DeleteBehavior.SetNull);
|
|
||||||
|
|
||||||
// AmtDevice SystemUuid 索引
|
|
||||||
modelBuilder.Entity<AmtDevice>()
|
|
||||||
.Property(d => d.SystemUuid)
|
|
||||||
.HasMaxLength(50);
|
|
||||||
|
|
||||||
modelBuilder.Entity<AmtDevice>()
|
|
||||||
.HasIndex(d => d.SystemUuid);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,738 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using AmtScanner.Api.Data;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace AmtScanner.Api.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(AppDbContext))]
|
|
||||||
[Migration("20260121062236_AddOsDeviceAndSystemUuid")]
|
|
||||||
partial class AddOsDeviceAndSystemUuid
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasAnnotation("ProductVersion", "8.0.0")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.AmtCredential", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<bool>("IsDefault")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.Property<string>("Password")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("Name");
|
|
||||||
|
|
||||||
b.ToTable("AmtCredentials");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.AmtDevice", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<bool>("AmtOnline")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<DateTime>("DiscoveredAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Hostname")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("IpAddress")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("varchar(50)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("LastSeenAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<int>("MajorVersion")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<int>("MinorVersion")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<bool>("OsOnline")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<int>("ProvisioningState")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("SystemUuid")
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("varchar(50)");
|
|
||||||
|
|
||||||
b.Property<string>("WindowsPassword")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("WindowsUsername")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("IpAddress")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.HasIndex("SystemUuid");
|
|
||||||
|
|
||||||
b.ToTable("AmtDevices");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.HardwareInfo", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<long>("DeviceId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<DateTime>("LastUpdated")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<int?>("ProcessorCores")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<int?>("ProcessorCurrentClockSpeed")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<int?>("ProcessorMaxClockSpeed")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("ProcessorModel")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<int?>("ProcessorThreads")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("SystemManufacturer")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("SystemModel")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("SystemSerialNumber")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<long?>("TotalMemoryBytes")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("DeviceId");
|
|
||||||
|
|
||||||
b.HasIndex("LastUpdated");
|
|
||||||
|
|
||||||
b.ToTable("HardwareInfos");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.MemoryModule", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<long?>("CapacityBytes")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<long>("HardwareInfoId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<string>("Manufacturer")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("MemoryType")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("PartNumber")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("SerialNumber")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("SlotLocation")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<int?>("SpeedMHz")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("HardwareInfoId");
|
|
||||||
|
|
||||||
b.ToTable("MemoryModules");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.Menu", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("AuthList")
|
|
||||||
.HasMaxLength(1000)
|
|
||||||
.HasColumnType("varchar(1000)");
|
|
||||||
|
|
||||||
b.Property<string>("Component")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Icon")
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("varchar(100)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsHide")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsHideTab")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsIframe")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsSystem")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<bool>("KeepAlive")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<string>("Link")
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("varchar(500)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("varchar(100)");
|
|
||||||
|
|
||||||
b.Property<int?>("ParentId")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("Path")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.Property<string>("Roles")
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("varchar(500)");
|
|
||||||
|
|
||||||
b.Property<int>("Sort")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("Title")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("Name");
|
|
||||||
|
|
||||||
b.HasIndex("ParentId");
|
|
||||||
|
|
||||||
b.ToTable("Menus");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.OsDevice", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<long?>("AmtDeviceId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<string>("Architecture")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<DateTime>("DiscoveredAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Hostname")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("IpAddress")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("varchar(50)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsOnline")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("LastBootTime")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("LastOnlineAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("LastUpdatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("LoggedInUser")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("MacAddress")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<int>("OsType")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("OsVersion")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("SystemUuid")
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("varchar(50)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("AmtDeviceId");
|
|
||||||
|
|
||||||
b.HasIndex("IpAddress")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.HasIndex("SystemUuid");
|
|
||||||
|
|
||||||
b.ToTable("OsDevices");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.RemoteAccessToken", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<long>("DeviceId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<DateTime>("ExpiresAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsUsed")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<int>("MaxUseCount")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("Note")
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("varchar(500)");
|
|
||||||
|
|
||||||
b.Property<string>("Token")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("varchar(64)");
|
|
||||||
|
|
||||||
b.Property<int>("UseCount")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("UsedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("DeviceId");
|
|
||||||
|
|
||||||
b.HasIndex("Token")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("RemoteAccessTokens");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.Role", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("varchar(500)");
|
|
||||||
|
|
||||||
b.Property<bool>("Enabled")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<string>("RoleCode")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("varchar(50)");
|
|
||||||
|
|
||||||
b.Property<string>("RoleName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("varchar(100)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("RoleCode")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("Roles");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.RoleMenu", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("RoleId")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<int>("MenuId")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.HasKey("RoleId", "MenuId");
|
|
||||||
|
|
||||||
b.HasIndex("MenuId");
|
|
||||||
|
|
||||||
b.ToTable("RoleMenus");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.StorageDevice", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<long?>("CapacityBytes")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<string>("DeviceId")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<long>("HardwareInfoId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<string>("InterfaceType")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("Model")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("HardwareInfoId");
|
|
||||||
|
|
||||||
b.ToTable("StorageDevices");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.User", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("Avatar")
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("varchar(500)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("CreatedBy")
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("varchar(100)");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.Property<string>("Gender")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(1)
|
|
||||||
.HasColumnType("varchar(1)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsDeleted")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<string>("NickName")
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("varchar(100)");
|
|
||||||
|
|
||||||
b.Property<string>("PasswordHash")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.Property<string>("Phone")
|
|
||||||
.HasMaxLength(20)
|
|
||||||
.HasColumnType("varchar(20)");
|
|
||||||
|
|
||||||
b.Property<string>("RefreshToken")
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("varchar(500)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("RefreshTokenExpiryTime")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Status")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(1)
|
|
||||||
.HasColumnType("varchar(1)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("UpdatedBy")
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("varchar(100)");
|
|
||||||
|
|
||||||
b.Property<string>("UserName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("varchar(100)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("UserName")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("Users");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.UserRole", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("UserId")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<int>("RoleId")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.HasKey("UserId", "RoleId");
|
|
||||||
|
|
||||||
b.HasIndex("RoleId");
|
|
||||||
|
|
||||||
b.ToTable("UserRoles");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.WindowsCredential", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Domain")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsDefault")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.Property<string>("Note")
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("varchar(500)");
|
|
||||||
|
|
||||||
b.Property<string>("Password")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("varchar(500)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("Name");
|
|
||||||
|
|
||||||
b.ToTable("WindowsCredentials");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.HardwareInfo", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("AmtScanner.Api.Models.AmtDevice", "Device")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("DeviceId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Device");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.MemoryModule", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("AmtScanner.Api.Models.HardwareInfo", "HardwareInfo")
|
|
||||||
.WithMany("MemoryModules")
|
|
||||||
.HasForeignKey("HardwareInfoId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("HardwareInfo");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.Menu", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("AmtScanner.Api.Models.Menu", "Parent")
|
|
||||||
.WithMany("Children")
|
|
||||||
.HasForeignKey("ParentId")
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
b.Navigation("Parent");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.OsDevice", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("AmtScanner.Api.Models.AmtDevice", "AmtDevice")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("AmtDeviceId")
|
|
||||||
.OnDelete(DeleteBehavior.SetNull);
|
|
||||||
|
|
||||||
b.Navigation("AmtDevice");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.RemoteAccessToken", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("AmtScanner.Api.Models.AmtDevice", "Device")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("DeviceId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Device");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.RoleMenu", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("AmtScanner.Api.Models.Menu", "Menu")
|
|
||||||
.WithMany("RoleMenus")
|
|
||||||
.HasForeignKey("MenuId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("AmtScanner.Api.Models.Role", "Role")
|
|
||||||
.WithMany("RoleMenus")
|
|
||||||
.HasForeignKey("RoleId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Menu");
|
|
||||||
|
|
||||||
b.Navigation("Role");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.StorageDevice", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("AmtScanner.Api.Models.HardwareInfo", "HardwareInfo")
|
|
||||||
.WithMany("StorageDevices")
|
|
||||||
.HasForeignKey("HardwareInfoId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("HardwareInfo");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.UserRole", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("AmtScanner.Api.Models.Role", "Role")
|
|
||||||
.WithMany("UserRoles")
|
|
||||||
.HasForeignKey("RoleId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("AmtScanner.Api.Models.User", "User")
|
|
||||||
.WithMany("UserRoles")
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Role");
|
|
||||||
|
|
||||||
b.Navigation("User");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.HardwareInfo", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("MemoryModules");
|
|
||||||
|
|
||||||
b.Navigation("StorageDevices");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.Menu", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Children");
|
|
||||||
|
|
||||||
b.Navigation("RoleMenus");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.Role", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("RoleMenus");
|
|
||||||
|
|
||||||
b.Navigation("UserRoles");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.User", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("UserRoles");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,102 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace AmtScanner.Api.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddOsDeviceAndSystemUuid : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "SystemUuid",
|
|
||||||
table: "AmtDevices",
|
|
||||||
type: "varchar(50)",
|
|
||||||
maxLength: 50,
|
|
||||||
nullable: true)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4");
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "OsDevices",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<long>(type: "bigint", nullable: false)
|
|
||||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
|
||||||
IpAddress = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
SystemUuid = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: true)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
Hostname = table.Column<string>(type: "longtext", nullable: true)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
OsType = table.Column<int>(type: "int", nullable: false),
|
|
||||||
OsVersion = table.Column<string>(type: "longtext", nullable: true)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
Architecture = table.Column<string>(type: "longtext", nullable: true)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
LoggedInUser = table.Column<string>(type: "longtext", nullable: true)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
LastBootTime = table.Column<DateTime>(type: "datetime(6)", nullable: true),
|
|
||||||
MacAddress = table.Column<string>(type: "longtext", nullable: true)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
IsOnline = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
|
||||||
LastOnlineAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
|
|
||||||
DiscoveredAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
|
||||||
LastUpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
|
||||||
Description = table.Column<string>(type: "longtext", nullable: true)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
AmtDeviceId = table.Column<long>(type: "bigint", nullable: true)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_OsDevices", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_OsDevices_AmtDevices_AmtDeviceId",
|
|
||||||
column: x => x.AmtDeviceId,
|
|
||||||
principalTable: "AmtDevices",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.SetNull);
|
|
||||||
})
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_AmtDevices_SystemUuid",
|
|
||||||
table: "AmtDevices",
|
|
||||||
column: "SystemUuid");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_OsDevices_AmtDeviceId",
|
|
||||||
table: "OsDevices",
|
|
||||||
column: "AmtDeviceId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_OsDevices_IpAddress",
|
|
||||||
table: "OsDevices",
|
|
||||||
column: "IpAddress",
|
|
||||||
unique: true);
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_OsDevices_SystemUuid",
|
|
||||||
table: "OsDevices",
|
|
||||||
column: "SystemUuid");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "OsDevices");
|
|
||||||
|
|
||||||
migrationBuilder.DropIndex(
|
|
||||||
name: "IX_AmtDevices_SystemUuid",
|
|
||||||
table: "AmtDevices");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "SystemUuid",
|
|
||||||
table: "AmtDevices");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,741 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using AmtScanner.Api.Data;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace AmtScanner.Api.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(AppDbContext))]
|
|
||||||
[Migration("20260121071149_AddSystemUuidToHardwareInfo")]
|
|
||||||
partial class AddSystemUuidToHardwareInfo
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasAnnotation("ProductVersion", "8.0.0")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.AmtCredential", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<bool>("IsDefault")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.Property<string>("Password")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("Name");
|
|
||||||
|
|
||||||
b.ToTable("AmtCredentials");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.AmtDevice", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<bool>("AmtOnline")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<DateTime>("DiscoveredAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Hostname")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("IpAddress")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("varchar(50)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("LastSeenAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<int>("MajorVersion")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<int>("MinorVersion")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<bool>("OsOnline")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<int>("ProvisioningState")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("SystemUuid")
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("varchar(50)");
|
|
||||||
|
|
||||||
b.Property<string>("WindowsPassword")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("WindowsUsername")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("IpAddress")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.HasIndex("SystemUuid");
|
|
||||||
|
|
||||||
b.ToTable("AmtDevices");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.HardwareInfo", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<long>("DeviceId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<DateTime>("LastUpdated")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<int?>("ProcessorCores")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<int?>("ProcessorCurrentClockSpeed")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<int?>("ProcessorMaxClockSpeed")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("ProcessorModel")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<int?>("ProcessorThreads")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("SystemManufacturer")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("SystemModel")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("SystemSerialNumber")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("SystemUuid")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<long?>("TotalMemoryBytes")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("DeviceId");
|
|
||||||
|
|
||||||
b.HasIndex("LastUpdated");
|
|
||||||
|
|
||||||
b.ToTable("HardwareInfos");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.MemoryModule", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<long?>("CapacityBytes")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<long>("HardwareInfoId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<string>("Manufacturer")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("MemoryType")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("PartNumber")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("SerialNumber")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("SlotLocation")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<int?>("SpeedMHz")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("HardwareInfoId");
|
|
||||||
|
|
||||||
b.ToTable("MemoryModules");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.Menu", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("AuthList")
|
|
||||||
.HasMaxLength(1000)
|
|
||||||
.HasColumnType("varchar(1000)");
|
|
||||||
|
|
||||||
b.Property<string>("Component")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Icon")
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("varchar(100)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsHide")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsHideTab")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsIframe")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsSystem")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<bool>("KeepAlive")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<string>("Link")
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("varchar(500)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("varchar(100)");
|
|
||||||
|
|
||||||
b.Property<int?>("ParentId")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("Path")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.Property<string>("Roles")
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("varchar(500)");
|
|
||||||
|
|
||||||
b.Property<int>("Sort")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("Title")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("Name");
|
|
||||||
|
|
||||||
b.HasIndex("ParentId");
|
|
||||||
|
|
||||||
b.ToTable("Menus");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.OsDevice", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<long?>("AmtDeviceId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<string>("Architecture")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<DateTime>("DiscoveredAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Hostname")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("IpAddress")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("varchar(50)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsOnline")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("LastBootTime")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("LastOnlineAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("LastUpdatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("LoggedInUser")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("MacAddress")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<int>("OsType")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("OsVersion")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("SystemUuid")
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("varchar(50)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("AmtDeviceId");
|
|
||||||
|
|
||||||
b.HasIndex("IpAddress")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.HasIndex("SystemUuid");
|
|
||||||
|
|
||||||
b.ToTable("OsDevices");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.RemoteAccessToken", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<long>("DeviceId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<DateTime>("ExpiresAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsUsed")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<int>("MaxUseCount")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("Note")
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("varchar(500)");
|
|
||||||
|
|
||||||
b.Property<string>("Token")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("varchar(64)");
|
|
||||||
|
|
||||||
b.Property<int>("UseCount")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("UsedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("DeviceId");
|
|
||||||
|
|
||||||
b.HasIndex("Token")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("RemoteAccessTokens");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.Role", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("varchar(500)");
|
|
||||||
|
|
||||||
b.Property<bool>("Enabled")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<string>("RoleCode")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("varchar(50)");
|
|
||||||
|
|
||||||
b.Property<string>("RoleName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("varchar(100)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("RoleCode")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("Roles");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.RoleMenu", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("RoleId")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<int>("MenuId")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.HasKey("RoleId", "MenuId");
|
|
||||||
|
|
||||||
b.HasIndex("MenuId");
|
|
||||||
|
|
||||||
b.ToTable("RoleMenus");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.StorageDevice", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<long?>("CapacityBytes")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<string>("DeviceId")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<long>("HardwareInfoId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<string>("InterfaceType")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("Model")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("HardwareInfoId");
|
|
||||||
|
|
||||||
b.ToTable("StorageDevices");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.User", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("Avatar")
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("varchar(500)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("CreatedBy")
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("varchar(100)");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.Property<string>("Gender")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(1)
|
|
||||||
.HasColumnType("varchar(1)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsDeleted")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<string>("NickName")
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("varchar(100)");
|
|
||||||
|
|
||||||
b.Property<string>("PasswordHash")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.Property<string>("Phone")
|
|
||||||
.HasMaxLength(20)
|
|
||||||
.HasColumnType("varchar(20)");
|
|
||||||
|
|
||||||
b.Property<string>("RefreshToken")
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("varchar(500)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("RefreshTokenExpiryTime")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Status")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(1)
|
|
||||||
.HasColumnType("varchar(1)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("UpdatedBy")
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("varchar(100)");
|
|
||||||
|
|
||||||
b.Property<string>("UserName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("varchar(100)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("UserName")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("Users");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.UserRole", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("UserId")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<int>("RoleId")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.HasKey("UserId", "RoleId");
|
|
||||||
|
|
||||||
b.HasIndex("RoleId");
|
|
||||||
|
|
||||||
b.ToTable("UserRoles");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.WindowsCredential", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Domain")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsDefault")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.Property<string>("Note")
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("varchar(500)");
|
|
||||||
|
|
||||||
b.Property<string>("Password")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(500)
|
|
||||||
.HasColumnType("varchar(500)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("Name");
|
|
||||||
|
|
||||||
b.ToTable("WindowsCredentials");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.HardwareInfo", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("AmtScanner.Api.Models.AmtDevice", "Device")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("DeviceId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Device");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.MemoryModule", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("AmtScanner.Api.Models.HardwareInfo", "HardwareInfo")
|
|
||||||
.WithMany("MemoryModules")
|
|
||||||
.HasForeignKey("HardwareInfoId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("HardwareInfo");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.Menu", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("AmtScanner.Api.Models.Menu", "Parent")
|
|
||||||
.WithMany("Children")
|
|
||||||
.HasForeignKey("ParentId")
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
b.Navigation("Parent");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.OsDevice", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("AmtScanner.Api.Models.AmtDevice", "AmtDevice")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("AmtDeviceId")
|
|
||||||
.OnDelete(DeleteBehavior.SetNull);
|
|
||||||
|
|
||||||
b.Navigation("AmtDevice");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.RemoteAccessToken", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("AmtScanner.Api.Models.AmtDevice", "Device")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("DeviceId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Device");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.RoleMenu", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("AmtScanner.Api.Models.Menu", "Menu")
|
|
||||||
.WithMany("RoleMenus")
|
|
||||||
.HasForeignKey("MenuId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("AmtScanner.Api.Models.Role", "Role")
|
|
||||||
.WithMany("RoleMenus")
|
|
||||||
.HasForeignKey("RoleId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Menu");
|
|
||||||
|
|
||||||
b.Navigation("Role");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.StorageDevice", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("AmtScanner.Api.Models.HardwareInfo", "HardwareInfo")
|
|
||||||
.WithMany("StorageDevices")
|
|
||||||
.HasForeignKey("HardwareInfoId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("HardwareInfo");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.UserRole", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("AmtScanner.Api.Models.Role", "Role")
|
|
||||||
.WithMany("UserRoles")
|
|
||||||
.HasForeignKey("RoleId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("AmtScanner.Api.Models.User", "User")
|
|
||||||
.WithMany("UserRoles")
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Role");
|
|
||||||
|
|
||||||
b.Navigation("User");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.HardwareInfo", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("MemoryModules");
|
|
||||||
|
|
||||||
b.Navigation("StorageDevices");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.Menu", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Children");
|
|
||||||
|
|
||||||
b.Navigation("RoleMenus");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.Role", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("RoleMenus");
|
|
||||||
|
|
||||||
b.Navigation("UserRoles");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.User", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("UserRoles");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace AmtScanner.Api.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddSystemUuidToHardwareInfo : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "SystemUuid",
|
|
||||||
table: "HardwareInfos",
|
|
||||||
type: "longtext",
|
|
||||||
nullable: true)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "SystemUuid",
|
|
||||||
table: "HardwareInfos");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -95,10 +95,6 @@ namespace AmtScanner.Api.Migrations
|
|||||||
b.Property<int>("ProvisioningState")
|
b.Property<int>("ProvisioningState")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("SystemUuid")
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("varchar(50)");
|
|
||||||
|
|
||||||
b.Property<string>("WindowsPassword")
|
b.Property<string>("WindowsPassword")
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
@ -110,8 +106,6 @@ namespace AmtScanner.Api.Migrations
|
|||||||
b.HasIndex("IpAddress")
|
b.HasIndex("IpAddress")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
b.HasIndex("SystemUuid");
|
|
||||||
|
|
||||||
b.ToTable("AmtDevices");
|
b.ToTable("AmtDevices");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -154,9 +148,6 @@ namespace AmtScanner.Api.Migrations
|
|||||||
b.Property<string>("SystemSerialNumber")
|
b.Property<string>("SystemSerialNumber")
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
b.Property<string>("SystemUuid")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<long?>("TotalMemoryBytes")
|
b.Property<long?>("TotalMemoryBytes")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
@ -282,72 +273,6 @@ namespace AmtScanner.Api.Migrations
|
|||||||
b.ToTable("Menus");
|
b.ToTable("Menus");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.OsDevice", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<long?>("AmtDeviceId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<string>("Architecture")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<DateTime>("DiscoveredAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("Hostname")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("IpAddress")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("varchar(50)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsOnline")
|
|
||||||
.HasColumnType("tinyint(1)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("LastBootTime")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("LastOnlineAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("LastUpdatedAt")
|
|
||||||
.HasColumnType("datetime(6)");
|
|
||||||
|
|
||||||
b.Property<string>("LoggedInUser")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("MacAddress")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<int>("OsType")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("OsVersion")
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("SystemUuid")
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("varchar(50)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("AmtDeviceId");
|
|
||||||
|
|
||||||
b.HasIndex("IpAddress")
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.HasIndex("SystemUuid");
|
|
||||||
|
|
||||||
b.ToTable("OsDevices");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.RemoteAccessToken", b =>
|
modelBuilder.Entity("AmtScanner.Api.Models.RemoteAccessToken", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
@ -637,16 +562,6 @@ namespace AmtScanner.Api.Migrations
|
|||||||
b.Navigation("Parent");
|
b.Navigation("Parent");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.OsDevice", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("AmtScanner.Api.Models.AmtDevice", "AmtDevice")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("AmtDeviceId")
|
|
||||||
.OnDelete(DeleteBehavior.SetNull);
|
|
||||||
|
|
||||||
b.Navigation("AmtDevice");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("AmtScanner.Api.Models.RemoteAccessToken", b =>
|
modelBuilder.Entity("AmtScanner.Api.Models.RemoteAccessToken", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("AmtScanner.Api.Models.AmtDevice", "Device")
|
b.HasOne("AmtScanner.Api.Models.AmtDevice", "Device")
|
||||||
|
|||||||
@ -12,11 +12,6 @@ public class AmtDevice
|
|||||||
|
|
||||||
public string? Hostname { get; set; }
|
public string? Hostname { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 系统 UUID(SMBIOS UUID,用于与操作系统绑定)
|
|
||||||
/// </summary>
|
|
||||||
public string? SystemUuid { get; set; }
|
|
||||||
|
|
||||||
public int MajorVersion { get; set; }
|
public int MajorVersion { get; set; }
|
||||||
|
|
||||||
public int MinorVersion { get; set; }
|
public int MinorVersion { get; set; }
|
||||||
|
|||||||
@ -19,11 +19,6 @@ public class HardwareInfo
|
|||||||
public string? SystemModel { get; set; }
|
public string? SystemModel { get; set; }
|
||||||
public string? SystemSerialNumber { get; set; }
|
public string? SystemSerialNumber { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 系统 UUID(SMBIOS UUID,用于与操作系统绑定)
|
|
||||||
/// </summary>
|
|
||||||
public string? SystemUuid { get; set; }
|
|
||||||
|
|
||||||
// Processor Information
|
// Processor Information
|
||||||
public string? ProcessorModel { get; set; }
|
public string? ProcessorModel { get; set; }
|
||||||
public int? ProcessorCores { get; set; }
|
public int? ProcessorCores { get; set; }
|
||||||
|
|||||||
@ -16,7 +16,6 @@ public class SystemInfoDto
|
|||||||
public string? Manufacturer { get; set; }
|
public string? Manufacturer { get; set; }
|
||||||
public string? Model { get; set; }
|
public string? Model { get; set; }
|
||||||
public string? SerialNumber { get; set; }
|
public string? SerialNumber { get; set; }
|
||||||
public string? Uuid { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ProcessorInfoDto
|
public class ProcessorInfoDto
|
||||||
|
|||||||
@ -1,103 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace AmtScanner.Api.Models;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 操作系统设备实体
|
|
||||||
/// </summary>
|
|
||||||
public class OsDevice
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
public long Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IP 地址
|
|
||||||
/// </summary>
|
|
||||||
[Required]
|
|
||||||
public string IpAddress { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 系统 UUID(SMBIOS UUID,用于与 AMT 绑定)
|
|
||||||
/// </summary>
|
|
||||||
public string? SystemUuid { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 主机名
|
|
||||||
/// </summary>
|
|
||||||
public string? Hostname { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 操作系统类型
|
|
||||||
/// </summary>
|
|
||||||
public OsType OsType { get; set; } = OsType.Unknown;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 操作系统版本
|
|
||||||
/// </summary>
|
|
||||||
public string? OsVersion { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 操作系统架构
|
|
||||||
/// </summary>
|
|
||||||
public string? Architecture { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 当前登录用户
|
|
||||||
/// </summary>
|
|
||||||
public string? LoggedInUser { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 系统最后启动时间
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? LastBootTime { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// MAC 地址
|
|
||||||
/// </summary>
|
|
||||||
public string? MacAddress { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否在线
|
|
||||||
/// </summary>
|
|
||||||
public bool IsOnline { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 最后在线时间
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? LastOnlineAt { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 发现时间
|
|
||||||
/// </summary>
|
|
||||||
public DateTime DiscoveredAt { get; set; } = DateTime.UtcNow;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 最后更新时间
|
|
||||||
/// </summary>
|
|
||||||
public DateTime LastUpdatedAt { get; set; } = DateTime.UtcNow;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 备注
|
|
||||||
/// </summary>
|
|
||||||
public string? Description { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 关联的 AMT 设备 ID
|
|
||||||
/// </summary>
|
|
||||||
public long? AmtDeviceId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 关联的 AMT 设备
|
|
||||||
/// </summary>
|
|
||||||
[ForeignKey(nameof(AmtDeviceId))]
|
|
||||||
public AmtDevice? AmtDevice { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum OsType
|
|
||||||
{
|
|
||||||
Unknown = 0,
|
|
||||||
Windows = 1,
|
|
||||||
Linux = 2,
|
|
||||||
MacOS = 3
|
|
||||||
}
|
|
||||||
@ -60,7 +60,6 @@ builder.Services.AddScoped<IAmtHardwareQueryService, AmtHardwareQueryService>();
|
|||||||
builder.Services.AddScoped<IHardwareInfoService, HardwareInfoService>();
|
builder.Services.AddScoped<IHardwareInfoService, HardwareInfoService>();
|
||||||
builder.Services.AddScoped<IHardwareInfoRepository, HardwareInfoRepository>();
|
builder.Services.AddScoped<IHardwareInfoRepository, HardwareInfoRepository>();
|
||||||
builder.Services.AddScoped<IAmtPowerService, AmtPowerService>();
|
builder.Services.AddScoped<IAmtPowerService, AmtPowerService>();
|
||||||
builder.Services.AddScoped<IWindowsScannerService, WindowsScannerService>();
|
|
||||||
builder.Services.AddHttpClient<IGuacamoleService, GuacamoleService>();
|
builder.Services.AddHttpClient<IGuacamoleService, GuacamoleService>();
|
||||||
|
|
||||||
// Add JWT Configuration
|
// Add JWT Configuration
|
||||||
|
|||||||
@ -44,7 +44,6 @@ public class HardwareInfoRepository : IHardwareInfoRepository
|
|||||||
existing.SystemManufacturer = hardwareInfo.SystemManufacturer;
|
existing.SystemManufacturer = hardwareInfo.SystemManufacturer;
|
||||||
existing.SystemModel = hardwareInfo.SystemModel;
|
existing.SystemModel = hardwareInfo.SystemModel;
|
||||||
existing.SystemSerialNumber = hardwareInfo.SystemSerialNumber;
|
existing.SystemSerialNumber = hardwareInfo.SystemSerialNumber;
|
||||||
existing.SystemUuid = hardwareInfo.SystemUuid; // 保存 UUID
|
|
||||||
existing.ProcessorModel = hardwareInfo.ProcessorModel;
|
existing.ProcessorModel = hardwareInfo.ProcessorModel;
|
||||||
existing.ProcessorCores = hardwareInfo.ProcessorCores;
|
existing.ProcessorCores = hardwareInfo.ProcessorCores;
|
||||||
existing.ProcessorThreads = hardwareInfo.ProcessorThreads;
|
existing.ProcessorThreads = hardwareInfo.ProcessorThreads;
|
||||||
|
|||||||
@ -152,9 +152,6 @@ public class AmtHardwareQueryService : IAmtHardwareQueryService
|
|||||||
|
|
||||||
_logger.LogDebug("System info: {Manufacturer} {Model}",
|
_logger.LogDebug("System info: {Manufacturer} {Model}",
|
||||||
hardwareInfo.SystemManufacturer, hardwareInfo.SystemModel);
|
hardwareInfo.SystemManufacturer, hardwareInfo.SystemModel);
|
||||||
|
|
||||||
// Query UUID from CIM_ComputerSystemPackage (在同一个 try 块内查询,确保连接有效)
|
|
||||||
QuerySystemUuid(connection, hardwareInfo);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -162,159 +159,6 @@ public class AmtHardwareQueryService : IAmtHardwareQueryService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void QuerySystemUuid(IWsmanConnection connection, HardwareInfo hardwareInfo)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_logger.LogInformation("Querying system UUID");
|
|
||||||
|
|
||||||
// 通过 CIM_ComputerSystemPackage 获取 PlatformGUID (UUID)
|
|
||||||
var query = connection.ExecQuery("SELECT * FROM CIM_ComputerSystemPackage");
|
|
||||||
|
|
||||||
foreach (IWsmanItem item in query)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 尝试获取 PlatformGUID
|
|
||||||
var platformGuid = item.Object.GetProperty("PlatformGUID");
|
|
||||||
_logger.LogInformation("PlatformGUID IsNull: {IsNull}, Value: {Value}",
|
|
||||||
platformGuid.IsNull, platformGuid.IsNull ? "null" : platformGuid.ToString());
|
|
||||||
if (!platformGuid.IsNull && !string.IsNullOrWhiteSpace(platformGuid.ToString()))
|
|
||||||
{
|
|
||||||
var rawGuid = platformGuid.ToString().Trim();
|
|
||||||
// 将 PlatformGUID 转换为标准 UUID 格式
|
|
||||||
var formattedUuid = FormatPlatformGuidToUuid(rawGuid);
|
|
||||||
hardwareInfo.SystemUuid = formattedUuid;
|
|
||||||
_logger.LogInformation("Found UUID from CIM_ComputerSystemPackage.PlatformGUID: Raw={Raw}, Formatted={Formatted}",
|
|
||||||
rawGuid, formattedUuid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogInformation(ex, "PlatformGUID not available from CIM_ComputerSystemPackage");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 备选:尝试从 Antecedent (CIM_Chassis) 获取 UUID
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var antecedent = item.Object.GetProperty("Antecedent");
|
|
||||||
_logger.LogInformation("Antecedent IsNull: {IsNull}, IsA CIM_Chassis: {IsChassis}",
|
|
||||||
antecedent.IsNull, !antecedent.IsNull && antecedent.IsA("CIM_Chassis"));
|
|
||||||
if (!antecedent.IsNull && antecedent.IsA("CIM_Chassis"))
|
|
||||||
{
|
|
||||||
var chassisObj = antecedent.Ref.Get();
|
|
||||||
|
|
||||||
var uuid = chassisObj.GetProperty("UUID");
|
|
||||||
_logger.LogInformation("CIM_Chassis UUID IsNull: {IsNull}, Value: {Value}",
|
|
||||||
uuid.IsNull, uuid.IsNull ? "null" : uuid.ToString());
|
|
||||||
if (!uuid.IsNull && !string.IsNullOrWhiteSpace(uuid.ToString()))
|
|
||||||
{
|
|
||||||
hardwareInfo.SystemUuid = uuid.ToString().Trim();
|
|
||||||
_logger.LogInformation("Found UUID from CIM_Chassis: {Uuid}", hardwareInfo.SystemUuid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogInformation(ex, "UUID not available from CIM_Chassis");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 备选方案:尝试从 CIM_PhysicalPackage 获取
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_logger.LogInformation("Trying CIM_PhysicalPackage for UUID");
|
|
||||||
var physicalQuery = connection.ExecQuery("SELECT * FROM CIM_PhysicalPackage");
|
|
||||||
foreach (IWsmanItem item in physicalQuery)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var uuid = item.Object.GetProperty("UUID");
|
|
||||||
_logger.LogInformation("CIM_PhysicalPackage UUID IsNull: {IsNull}, Value: {Value}",
|
|
||||||
uuid.IsNull, uuid.IsNull ? "null" : uuid.ToString());
|
|
||||||
if (!uuid.IsNull && !string.IsNullOrWhiteSpace(uuid.ToString()))
|
|
||||||
{
|
|
||||||
hardwareInfo.SystemUuid = uuid.ToString().Trim();
|
|
||||||
_logger.LogInformation("Found UUID from CIM_PhysicalPackage: {Uuid}", hardwareInfo.SystemUuid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogInformation(ex, "UUID property not available from CIM_PhysicalPackage item");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogInformation(ex, "Failed to query CIM_PhysicalPackage for UUID");
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogWarning("Could not find system UUID from any source");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "Failed to query system UUID");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将 AMT PlatformGUID 转换为标准 UUID 格式
|
|
||||||
/// PlatformGUID 是 32 位十六进制字符串,需要转换为 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 格式
|
|
||||||
///
|
|
||||||
/// 示例:
|
|
||||||
/// AMT PlatformGUID: B826D58CD3E2E31187820B47ABD01400
|
|
||||||
/// Windows UUID: 8CD526B8-E2D3-11E3-8782-0B47ABD01400
|
|
||||||
///
|
|
||||||
/// 转换规则:前三组需要按字节反转(每2个十六进制字符为1字节)
|
|
||||||
/// </summary>
|
|
||||||
private string FormatPlatformGuidToUuid(string platformGuid)
|
|
||||||
{
|
|
||||||
// 移除可能存在的连字符
|
|
||||||
var cleanGuid = platformGuid.Replace("-", "").ToUpperInvariant();
|
|
||||||
|
|
||||||
if (cleanGuid.Length != 32)
|
|
||||||
{
|
|
||||||
_logger.LogWarning("Invalid PlatformGUID length: {Length}, expected 32", cleanGuid.Length);
|
|
||||||
return platformGuid; // 返回原始值
|
|
||||||
}
|
|
||||||
|
|
||||||
// AMT PlatformGUID 格式(32字符):
|
|
||||||
// B826D58C D3E2 E311 8782 0B47ABD01400
|
|
||||||
// 位置: 0-7 8-11 12-15 16-19 20-31
|
|
||||||
|
|
||||||
// 标准 UUID 格式:
|
|
||||||
// 8CD526B8-E2D3-11E3-8782-0B47ABD01400
|
|
||||||
|
|
||||||
// 第一组:4 字节(8字符),按字节反转
|
|
||||||
// B826D58C -> 8C D5 26 B8 -> 8CD526B8
|
|
||||||
var part1 = cleanGuid.Substring(0, 8);
|
|
||||||
var part1Reversed = $"{part1[6]}{part1[7]}{part1[4]}{part1[5]}{part1[2]}{part1[3]}{part1[0]}{part1[1]}";
|
|
||||||
|
|
||||||
// 第二组:2 字节(4字符),按字节反转
|
|
||||||
// D3E2 -> E2 D3 -> E2D3
|
|
||||||
var part2 = cleanGuid.Substring(8, 4);
|
|
||||||
var part2Reversed = $"{part2[2]}{part2[3]}{part2[0]}{part2[1]}";
|
|
||||||
|
|
||||||
// 第三组:2 字节(4字符),按字节反转
|
|
||||||
// E311 -> 11 E3 -> 11E3
|
|
||||||
var part3 = cleanGuid.Substring(12, 4);
|
|
||||||
var part3Reversed = $"{part3[2]}{part3[3]}{part3[0]}{part3[1]}";
|
|
||||||
|
|
||||||
// 第四组:2 字节(4字符),不反转
|
|
||||||
var part4 = cleanGuid.Substring(16, 4);
|
|
||||||
|
|
||||||
// 第五组:6 字节(12字符),不反转
|
|
||||||
var part5 = cleanGuid.Substring(20, 12);
|
|
||||||
|
|
||||||
var result = $"{part1Reversed}-{part2Reversed}-{part3Reversed}-{part4}-{part5}";
|
|
||||||
_logger.LogInformation("UUID conversion: {Raw} -> {Formatted}", cleanGuid, result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void QueryProcessorInfo(IWsmanConnection connection, HardwareInfo hardwareInfo)
|
private void QueryProcessorInfo(IWsmanConnection connection, HardwareInfo hardwareInfo)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@ -89,15 +89,6 @@ public class HardwareInfoService : IHardwareInfoService
|
|||||||
|
|
||||||
hardwareInfo.DeviceId = deviceId;
|
hardwareInfo.DeviceId = deviceId;
|
||||||
|
|
||||||
// 如果查询到了 UUID,保存到 AmtDevice
|
|
||||||
if (!string.IsNullOrEmpty(hardwareInfo.SystemUuid) && device.SystemUuid != hardwareInfo.SystemUuid)
|
|
||||||
{
|
|
||||||
device.SystemUuid = hardwareInfo.SystemUuid;
|
|
||||||
context.AmtDevices.Update(device);
|
|
||||||
await context.SaveChangesAsync();
|
|
||||||
_logger.LogInformation("Updated device {DeviceId} with UUID: {Uuid}", deviceId, hardwareInfo.SystemUuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save to cache
|
// Save to cache
|
||||||
await _repository.SaveAsync(hardwareInfo);
|
await _repository.SaveAsync(hardwareInfo);
|
||||||
|
|
||||||
@ -158,8 +149,7 @@ public class HardwareInfoService : IHardwareInfoService
|
|||||||
{
|
{
|
||||||
Manufacturer = hardwareInfo.SystemManufacturer,
|
Manufacturer = hardwareInfo.SystemManufacturer,
|
||||||
Model = hardwareInfo.SystemModel,
|
Model = hardwareInfo.SystemModel,
|
||||||
SerialNumber = hardwareInfo.SystemSerialNumber,
|
SerialNumber = hardwareInfo.SystemSerialNumber
|
||||||
Uuid = hardwareInfo.SystemUuid
|
|
||||||
},
|
},
|
||||||
Processor = new ProcessorInfoDto
|
Processor = new ProcessorInfoDto
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,465 +0,0 @@
|
|||||||
using AmtScanner.Api.Data;
|
|
||||||
using AmtScanner.Api.Models;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Management;
|
|
||||||
using System.Net.NetworkInformation;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
|
|
||||||
namespace AmtScanner.Api.Services;
|
|
||||||
|
|
||||||
public interface IWindowsScannerService
|
|
||||||
{
|
|
||||||
Task<List<OsDevice>> ScanNetworkAsync(string taskId, string networkSegment, string subnetMask,
|
|
||||||
IProgress<OsScanProgress> progress, CancellationToken cancellationToken = default);
|
|
||||||
Task<OsDevice?> GetOsInfoAsync(string ipAddress, string username, string password);
|
|
||||||
Task<string?> GetSystemUuidAsync(string ipAddress, string username, string password);
|
|
||||||
Task BindAmtDevicesAsync();
|
|
||||||
void CancelScan(string taskId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class WindowsScannerService : IWindowsScannerService
|
|
||||||
{
|
|
||||||
private readonly IServiceScopeFactory _scopeFactory;
|
|
||||||
private readonly ILogger<WindowsScannerService> _logger;
|
|
||||||
private readonly IConfiguration _configuration;
|
|
||||||
private readonly ConcurrentDictionary<string, CancellationTokenSource> _cancellationTokens = new();
|
|
||||||
|
|
||||||
public WindowsScannerService(
|
|
||||||
IServiceScopeFactory scopeFactory,
|
|
||||||
ILogger<WindowsScannerService> logger,
|
|
||||||
IConfiguration configuration)
|
|
||||||
{
|
|
||||||
_scopeFactory = scopeFactory;
|
|
||||||
_logger = logger;
|
|
||||||
_configuration = configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<OsDevice>> ScanNetworkAsync(
|
|
||||||
string taskId,
|
|
||||||
string networkSegment,
|
|
||||||
string subnetMask,
|
|
||||||
IProgress<OsScanProgress> progress,
|
|
||||||
CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("Starting OS scan for task: {TaskId}", taskId);
|
|
||||||
|
|
||||||
var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
|
||||||
_cancellationTokens[taskId] = cts;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var ipList = CalculateIpRange(networkSegment, subnetMask);
|
|
||||||
var foundDevices = new ConcurrentBag<OsDevice>();
|
|
||||||
int scannedCount = 0;
|
|
||||||
int foundCount = 0;
|
|
||||||
|
|
||||||
var threadPoolSize = _configuration.GetValue<int>("Scanner:ThreadPoolSize", 50);
|
|
||||||
var parallelOptions = new ParallelOptions
|
|
||||||
{
|
|
||||||
MaxDegreeOfParallelism = threadPoolSize,
|
|
||||||
CancellationToken = cts.Token
|
|
||||||
};
|
|
||||||
|
|
||||||
await Parallel.ForEachAsync(ipList, parallelOptions, async (ip, ct) =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var device = await ScanSingleHostAsync(ip, ct);
|
|
||||||
var scanned = Interlocked.Increment(ref scannedCount);
|
|
||||||
|
|
||||||
if (device != null)
|
|
||||||
{
|
|
||||||
foundDevices.Add(device);
|
|
||||||
var found = Interlocked.Increment(ref foundCount);
|
|
||||||
await SaveOsDeviceAsync(device);
|
|
||||||
|
|
||||||
progress.Report(new OsScanProgress
|
|
||||||
{
|
|
||||||
TaskId = taskId,
|
|
||||||
ScannedCount = scanned,
|
|
||||||
TotalCount = ipList.Count,
|
|
||||||
FoundDevices = found,
|
|
||||||
ProgressPercentage = (double)scanned / ipList.Count * 100,
|
|
||||||
CurrentIp = ip,
|
|
||||||
LatestDevice = device
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
progress.Report(new OsScanProgress
|
|
||||||
{
|
|
||||||
TaskId = taskId,
|
|
||||||
ScannedCount = scanned,
|
|
||||||
TotalCount = ipList.Count,
|
|
||||||
FoundDevices = foundCount,
|
|
||||||
ProgressPercentage = (double)scanned / ipList.Count * 100,
|
|
||||||
CurrentIp = ip
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogDebug(ex, "Error scanning {Ip}", ip);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 扫描完成后尝试绑定 AMT 设备
|
|
||||||
await BindAmtDevicesAsync();
|
|
||||||
|
|
||||||
return foundDevices.ToList();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_cancellationTokens.TryRemove(taskId, out _);
|
|
||||||
cts.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CancelScan(string taskId)
|
|
||||||
{
|
|
||||||
if (_cancellationTokens.TryGetValue(taskId, out var cts))
|
|
||||||
{
|
|
||||||
cts.Cancel();
|
|
||||||
_logger.LogInformation("OS scan task {TaskId} cancelled", taskId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<OsDevice?> ScanSingleHostAsync(string ip, CancellationToken ct)
|
|
||||||
{
|
|
||||||
// 先 Ping 检测是否在线
|
|
||||||
if (!await IsHostOnlineAsync(ip, ct))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// 检测 Windows 端口
|
|
||||||
var isWindows = await IsWindowsHostAsync(ip, ct);
|
|
||||||
|
|
||||||
if (isWindows)
|
|
||||||
{
|
|
||||||
return new OsDevice
|
|
||||||
{
|
|
||||||
IpAddress = ip,
|
|
||||||
OsType = OsType.Windows,
|
|
||||||
IsOnline = true,
|
|
||||||
LastOnlineAt = DateTime.UtcNow,
|
|
||||||
DiscoveredAt = DateTime.UtcNow,
|
|
||||||
LastUpdatedAt = DateTime.UtcNow,
|
|
||||||
Description = "通过端口扫描发现"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检测 Linux (SSH 端口)
|
|
||||||
var isLinux = await IsPortOpenAsync(ip, 22, 2000, ct);
|
|
||||||
if (isLinux)
|
|
||||||
{
|
|
||||||
return new OsDevice
|
|
||||||
{
|
|
||||||
IpAddress = ip,
|
|
||||||
OsType = OsType.Linux,
|
|
||||||
IsOnline = true,
|
|
||||||
LastOnlineAt = DateTime.UtcNow,
|
|
||||||
DiscoveredAt = DateTime.UtcNow,
|
|
||||||
LastUpdatedAt = DateTime.UtcNow,
|
|
||||||
Description = "通过 SSH 端口发现"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<bool> IsHostOnlineAsync(string ip, CancellationToken ct)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var ping = new Ping();
|
|
||||||
var reply = await ping.SendPingAsync(ip, 1000);
|
|
||||||
return reply.Status == IPStatus.Success;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<bool> IsWindowsHostAsync(string ip, CancellationToken ct)
|
|
||||||
{
|
|
||||||
// 检测 Windows 常用端口: 135(RPC), 445(SMB), 3389(RDP), 5985(WinRM)
|
|
||||||
var windowsPorts = new[] { 135, 445, 3389, 5985 };
|
|
||||||
|
|
||||||
foreach (var port in windowsPorts)
|
|
||||||
{
|
|
||||||
if (await IsPortOpenAsync(ip, port, 1000, ct))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<bool> IsPortOpenAsync(string ip, int port, int timeoutMs, CancellationToken ct)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var client = new TcpClient();
|
|
||||||
using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
|
||||||
cts.CancelAfter(timeoutMs);
|
|
||||||
await client.ConnectAsync(ip, port, cts.Token);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 通过 WMI 获取远程 Windows 系统信息
|
|
||||||
/// </summary>
|
|
||||||
public async Task<OsDevice?> GetOsInfoAsync(string ipAddress, string username, string password)
|
|
||||||
{
|
|
||||||
return await Task.Run(() =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var options = new ConnectionOptions
|
|
||||||
{
|
|
||||||
Username = username,
|
|
||||||
Password = password,
|
|
||||||
Impersonation = ImpersonationLevel.Impersonate,
|
|
||||||
Authentication = AuthenticationLevel.PacketPrivacy
|
|
||||||
};
|
|
||||||
|
|
||||||
var scope = new ManagementScope($"\\\\{ipAddress}\\root\\cimv2", options);
|
|
||||||
scope.Connect();
|
|
||||||
|
|
||||||
var device = new OsDevice
|
|
||||||
{
|
|
||||||
IpAddress = ipAddress,
|
|
||||||
OsType = OsType.Windows,
|
|
||||||
IsOnline = true,
|
|
||||||
LastOnlineAt = DateTime.UtcNow,
|
|
||||||
LastUpdatedAt = DateTime.UtcNow
|
|
||||||
};
|
|
||||||
|
|
||||||
// 获取 UUID
|
|
||||||
var uuidQuery = new ObjectQuery("SELECT UUID FROM Win32_ComputerSystemProduct");
|
|
||||||
using (var uuidSearcher = new ManagementObjectSearcher(scope, uuidQuery))
|
|
||||||
{
|
|
||||||
foreach (var obj in uuidSearcher.Get())
|
|
||||||
{
|
|
||||||
device.SystemUuid = obj["UUID"]?.ToString();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取操作系统信息
|
|
||||||
var osQuery = new ObjectQuery("SELECT Caption, Version, OSArchitecture, LastBootUpTime FROM Win32_OperatingSystem");
|
|
||||||
using (var osSearcher = new ManagementObjectSearcher(scope, osQuery))
|
|
||||||
{
|
|
||||||
foreach (var obj in osSearcher.Get())
|
|
||||||
{
|
|
||||||
device.OsVersion = $"{obj["Caption"]} ({obj["Version"]})";
|
|
||||||
device.Architecture = obj["OSArchitecture"]?.ToString();
|
|
||||||
|
|
||||||
var lastBootStr = obj["LastBootUpTime"]?.ToString();
|
|
||||||
if (!string.IsNullOrEmpty(lastBootStr))
|
|
||||||
{
|
|
||||||
device.LastBootTime = ManagementDateTimeConverter.ToDateTime(lastBootStr);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取计算机名
|
|
||||||
var csQuery = new ObjectQuery("SELECT Name, UserName FROM Win32_ComputerSystem");
|
|
||||||
using (var csSearcher = new ManagementObjectSearcher(scope, csQuery))
|
|
||||||
{
|
|
||||||
foreach (var obj in csSearcher.Get())
|
|
||||||
{
|
|
||||||
device.Hostname = obj["Name"]?.ToString();
|
|
||||||
device.LoggedInUser = obj["UserName"]?.ToString();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取 MAC 地址
|
|
||||||
var netQuery = new ObjectQuery("SELECT MACAddress FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = True");
|
|
||||||
using (var netSearcher = new ManagementObjectSearcher(scope, netQuery))
|
|
||||||
{
|
|
||||||
foreach (var obj in netSearcher.Get())
|
|
||||||
{
|
|
||||||
var mac = obj["MACAddress"]?.ToString();
|
|
||||||
if (!string.IsNullOrEmpty(mac))
|
|
||||||
{
|
|
||||||
device.MacAddress = mac;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
device.Description = "通过 WMI 获取详细信息";
|
|
||||||
return device;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "Failed to get OS info for {Ip} via WMI", ipAddress);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取远程 Windows 系统的 UUID
|
|
||||||
/// </summary>
|
|
||||||
public async Task<string?> GetSystemUuidAsync(string ipAddress, string username, string password)
|
|
||||||
{
|
|
||||||
return await Task.Run(() =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var options = new ConnectionOptions
|
|
||||||
{
|
|
||||||
Username = username,
|
|
||||||
Password = password,
|
|
||||||
Impersonation = ImpersonationLevel.Impersonate,
|
|
||||||
Authentication = AuthenticationLevel.PacketPrivacy
|
|
||||||
};
|
|
||||||
|
|
||||||
var scope = new ManagementScope($"\\\\{ipAddress}\\root\\cimv2", options);
|
|
||||||
scope.Connect();
|
|
||||||
|
|
||||||
var query = new ObjectQuery("SELECT UUID FROM Win32_ComputerSystemProduct");
|
|
||||||
using var searcher = new ManagementObjectSearcher(scope, query);
|
|
||||||
|
|
||||||
foreach (var obj in searcher.Get())
|
|
||||||
{
|
|
||||||
return obj["UUID"]?.ToString();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "Failed to get UUID for {Ip}", ipAddress);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 根据 UUID 自动绑定 AMT 设备和操作系统设备
|
|
||||||
/// </summary>
|
|
||||||
public async Task BindAmtDevicesAsync()
|
|
||||||
{
|
|
||||||
using var scope = _scopeFactory.CreateScope();
|
|
||||||
var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
|
||||||
|
|
||||||
// 获取所有有 UUID 的操作系统设备
|
|
||||||
var osDevices = await context.OsDevices
|
|
||||||
.Where(o => o.SystemUuid != null && o.AmtDeviceId == null)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
// 获取所有有 UUID 的 AMT 设备
|
|
||||||
var amtDevices = await context.AmtDevices
|
|
||||||
.Where(a => a.SystemUuid != null)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
var amtUuidMap = amtDevices.ToDictionary(a => a.SystemUuid!, a => a);
|
|
||||||
|
|
||||||
foreach (var osDevice in osDevices)
|
|
||||||
{
|
|
||||||
if (osDevice.SystemUuid != null && amtUuidMap.TryGetValue(osDevice.SystemUuid, out var amtDevice))
|
|
||||||
{
|
|
||||||
osDevice.AmtDeviceId = amtDevice.Id;
|
|
||||||
_logger.LogInformation("Bound OS device {OsIp} to AMT device {AmtIp} via UUID {Uuid}",
|
|
||||||
osDevice.IpAddress, amtDevice.IpAddress, osDevice.SystemUuid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await context.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SaveOsDeviceAsync(OsDevice device)
|
|
||||||
{
|
|
||||||
using var scope = _scopeFactory.CreateScope();
|
|
||||||
var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
|
||||||
|
|
||||||
var existing = await context.OsDevices
|
|
||||||
.FirstOrDefaultAsync(d => d.IpAddress == device.IpAddress);
|
|
||||||
|
|
||||||
if (existing != null)
|
|
||||||
{
|
|
||||||
existing.OsType = device.OsType;
|
|
||||||
existing.IsOnline = device.IsOnline;
|
|
||||||
existing.LastOnlineAt = device.LastOnlineAt;
|
|
||||||
existing.LastUpdatedAt = DateTime.UtcNow;
|
|
||||||
if (!string.IsNullOrEmpty(device.SystemUuid))
|
|
||||||
existing.SystemUuid = device.SystemUuid;
|
|
||||||
if (!string.IsNullOrEmpty(device.Hostname))
|
|
||||||
existing.Hostname = device.Hostname;
|
|
||||||
if (!string.IsNullOrEmpty(device.OsVersion))
|
|
||||||
existing.OsVersion = device.OsVersion;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.OsDevices.Add(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
await context.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<string> CalculateIpRange(string networkSegment, string subnetMask)
|
|
||||||
{
|
|
||||||
var ipList = new List<string>();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var networkLong = IpToLong(networkSegment);
|
|
||||||
var cidr = SubnetMaskToCidr(subnetMask);
|
|
||||||
var hostBits = 32 - cidr;
|
|
||||||
var totalHosts = (int)Math.Pow(2, hostBits);
|
|
||||||
|
|
||||||
for (int i = 1; i < totalHosts - 1; i++)
|
|
||||||
{
|
|
||||||
var ipLong = networkLong + i;
|
|
||||||
ipList.Add(LongToIp(ipLong));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error calculating IP range");
|
|
||||||
}
|
|
||||||
return ipList;
|
|
||||||
}
|
|
||||||
|
|
||||||
private long IpToLong(string ipAddress)
|
|
||||||
{
|
|
||||||
var parts = ipAddress.Split('.');
|
|
||||||
long result = 0;
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
result = result << 8 | long.Parse(parts[i]);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string LongToIp(long ip) =>
|
|
||||||
$"{(ip >> 24) & 0xFF}.{(ip >> 16) & 0xFF}.{(ip >> 8) & 0xFF}.{ip & 0xFF}";
|
|
||||||
|
|
||||||
private int SubnetMaskToCidr(string subnetMask)
|
|
||||||
{
|
|
||||||
if (subnetMask.StartsWith("/"))
|
|
||||||
return int.Parse(subnetMask.Substring(1));
|
|
||||||
|
|
||||||
var parts = subnetMask.Split('.');
|
|
||||||
int cidr = 0;
|
|
||||||
foreach (var part in parts)
|
|
||||||
cidr += Convert.ToString(int.Parse(part), 2).Count(c => c == '1');
|
|
||||||
return cidr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class OsScanProgress
|
|
||||||
{
|
|
||||||
public string TaskId { get; set; } = string.Empty;
|
|
||||||
public int ScannedCount { get; set; }
|
|
||||||
public int TotalCount { get; set; }
|
|
||||||
public int FoundDevices { get; set; }
|
|
||||||
public double ProgressPercentage { get; set; }
|
|
||||||
public string? CurrentIp { get; set; }
|
|
||||||
public OsDevice? LatestDevice { get; set; }
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
-- 添加操作系统设备管理菜单(放在桌面管理目录下,ParentId=20)
|
|
||||||
INSERT INTO `Menus` (`Id`, `Name`, `Title`, `Icon`, `Path`, `Component`, `ParentId`, `Sort`, `IsHide`, `KeepAlive`, `IsIframe`, `IsSystem`, `IsHideTab`, `CreatedAt`)
|
|
||||||
VALUES (22, 'OsDevices', '操作系统', 'Monitor', 'os-devices', '/desktop-manage/os-devices', 20, 2, 0, 0, 0, 0, 0, NOW());
|
|
||||||
|
|
||||||
-- 为超级管理员角色添加菜单权限
|
|
||||||
INSERT INTO `RoleMenus` (`RoleId`, `MenuId`)
|
|
||||||
SELECT r.Id, 22 FROM `Roles` r WHERE r.RoleCode = 'R_SUPER';
|
|
||||||
|
|
||||||
-- 为管理员角色添加菜单权限
|
|
||||||
INSERT INTO `RoleMenus` (`RoleId`, `MenuId`)
|
|
||||||
SELECT r.Id, 22 FROM `Roles` r WHERE r.RoleCode = 'R_ADMIN';
|
|
||||||
Loading…
x
Reference in New Issue
Block a user