refactor(security): 重构安全配置并优化测试环境
- 移除旧的测试套件和UAT测试文件 - 更新密码编码器配置使用BCrypt strength=12 - 添加用户角色关联表和相关服务 - 优化前端日期显示格式 - 清理无用资源和配置文件 - 增强测试数据管理和清理功能
This commit is contained in:
@@ -9,7 +9,7 @@ export interface User {
|
||||
phone: string
|
||||
avatar: string
|
||||
status: UserStatus
|
||||
roles: string[]
|
||||
roles: number[]
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
export const formatDateTime = (dateTime: string | Date | null | undefined): string => {
|
||||
if (!dateTime) return '-'
|
||||
|
||||
try {
|
||||
const date = typeof dateTime === 'string' ? new Date(dateTime) : dateTime
|
||||
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||
} catch (error) {
|
||||
console.error('时间格式化失败:', error)
|
||||
return String(dateTime)
|
||||
}
|
||||
}
|
||||
|
||||
export const formatDate = (date: string | Date | null | undefined): string => {
|
||||
if (!date) return '-'
|
||||
|
||||
try {
|
||||
const d = typeof date === 'string' ? new Date(date) : date
|
||||
|
||||
const year = d.getFullYear()
|
||||
const month = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(d.getDate()).padStart(2, '0')
|
||||
|
||||
return `${year}-${month}-${day}`
|
||||
} catch (error) {
|
||||
console.error('日期格式化失败:', error)
|
||||
return String(date)
|
||||
}
|
||||
}
|
||||
|
||||
export const formatTime = (time: string | Date | null | undefined): string => {
|
||||
if (!time) return '-'
|
||||
|
||||
try {
|
||||
const t = typeof time === 'string' ? new Date(time) : time
|
||||
|
||||
const hours = String(t.getHours()).padStart(2, '0')
|
||||
const minutes = String(t.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(t.getSeconds()).padStart(2, '0')
|
||||
|
||||
return `${hours}:${minutes}:${seconds}`
|
||||
} catch (error) {
|
||||
console.error('时间格式化失败:', error)
|
||||
return String(time)
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,11 @@
|
||||
prop="loginTime"
|
||||
label="登录时间"
|
||||
sortable="custom"
|
||||
/>
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ formatDateTime(row.loginTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
v-model:current-page="pagination.current"
|
||||
@@ -96,6 +100,7 @@
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import request from '@/utils/request'
|
||||
import { formatDateTime } from '@/utils/dateFormat'
|
||||
|
||||
const loading = ref(false)
|
||||
const dataSource = ref([])
|
||||
|
||||
@@ -106,7 +106,11 @@
|
||||
prop="createdAt"
|
||||
label="操作时间"
|
||||
sortable="custom"
|
||||
/>
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ formatDateTime(row.createdAt) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
v-model:current-page="pagination.current"
|
||||
@@ -126,6 +130,7 @@
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { Search, User, Document, Setting, Lock, View, Edit, Delete, Plus, Download } from '@element-plus/icons-vue'
|
||||
import { operationLogApi, OperationLog } from '@/api/operationLog'
|
||||
import { formatDateTime } from '@/utils/dateFormat'
|
||||
|
||||
const loading = ref(false)
|
||||
const dataSource = ref<OperationLog[]>([])
|
||||
|
||||
@@ -58,7 +58,11 @@
|
||||
<el-table-column
|
||||
prop="createdAt"
|
||||
label="上传时间"
|
||||
/>
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ formatDateTime(row.createdAt) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="createBy"
|
||||
label="上传人"
|
||||
@@ -96,6 +100,7 @@ import { ref, onMounted, computed } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Upload, Search } from '@element-plus/icons-vue'
|
||||
import request from '@/utils/request'
|
||||
import { formatDateTime } from '@/utils/dateFormat'
|
||||
|
||||
const loading = ref(false)
|
||||
const dataSource = ref([])
|
||||
|
||||
@@ -56,7 +56,11 @@
|
||||
<el-table-column
|
||||
prop="createdAt"
|
||||
label="发布时间"
|
||||
/>
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ formatDateTime(row.createdAt) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
width="150"
|
||||
@@ -146,6 +150,7 @@
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import request from '@/utils/request'
|
||||
import { formatDateTime } from '@/utils/dateFormat'
|
||||
|
||||
const loading = ref(false)
|
||||
const dataSource = ref([])
|
||||
|
||||
@@ -191,17 +191,17 @@ const systemInfo = reactive({
|
||||
const fetchStats = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const userCountRes: any = await request.get('/users/count')
|
||||
stats.userCount = userCountRes || 0
|
||||
const [userCountRes, roleCountRes, todayLoginRes, operationLogRes] = await Promise.allSettled([
|
||||
request.get('/users/count'),
|
||||
request.get('/roles/count'),
|
||||
request.get('/logs/login/today/count'),
|
||||
request.get('/logs/operation/count')
|
||||
])
|
||||
|
||||
const roleCountRes: any = await request.get('/roles/count')
|
||||
stats.roleCount = roleCountRes || 0
|
||||
|
||||
const todayLoginRes: any = await request.get('/logs/login/today/count')
|
||||
stats.todayLogin = todayLoginRes || 0
|
||||
|
||||
const operationLogRes: any = await request.get('/logs/operation/count')
|
||||
stats.operationLog = operationLogRes || 0
|
||||
stats.userCount = userCountRes.status === 'fulfilled' ? (userCountRes.value || 0) : 0
|
||||
stats.roleCount = roleCountRes.status === 'fulfilled' ? (roleCountRes.value || 0) : 0
|
||||
stats.todayLogin = todayLoginRes.status === 'fulfilled' ? (todayLoginRes.value || 0) : 0
|
||||
stats.operationLog = operationLogRes.status === 'fulfilled' ? (operationLogRes.value || 0) : 0
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch stats:', error)
|
||||
} finally {
|
||||
@@ -210,14 +210,12 @@ const fetchStats = async () => {
|
||||
}
|
||||
|
||||
const fetchRecentLogins = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res: any = await request.get('/logs/login/recent?limit=10')
|
||||
recentLogins.value = res || []
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch recent logins:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
recentLogins.value = []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,11 @@
|
||||
prop="createdAt"
|
||||
label="创建时间"
|
||||
sortable="custom"
|
||||
/>
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ formatDateTime(row.createdAt) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
width="250"
|
||||
@@ -213,6 +217,7 @@ import { Search } from '@element-plus/icons-vue'
|
||||
import { roleApi, type Role, type CreateRoleRequest, type UpdateRoleRequest, type Permission } from '@/api/role.api'
|
||||
import { handleApiError } from '@/utils/errorHandler'
|
||||
import { RoleStatus } from '@/constants/status'
|
||||
import { formatDateTime } from '@/utils/dateFormat'
|
||||
|
||||
const loading = ref(false)
|
||||
const dataSource = ref<Role[]>([])
|
||||
|
||||
@@ -81,7 +81,11 @@
|
||||
prop="createdAt"
|
||||
label="创建时间"
|
||||
sortable="custom"
|
||||
/>
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ formatDateTime(row.createdAt) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
width="250"
|
||||
@@ -219,6 +223,7 @@ import { userApi, type User, type CreateUserRequest, type UpdateUserRequest } fr
|
||||
import { roleApi, type Role } from '@/api/role.api'
|
||||
import { handleApiError } from '@/utils/errorHandler'
|
||||
import { UserStatus, StatusHelper } from '@/constants/status'
|
||||
import { formatDateTime } from '@/utils/dateFormat'
|
||||
|
||||
const loading = ref(false)
|
||||
const dataSource = ref<User[]>([])
|
||||
@@ -376,12 +381,9 @@ const handleAssignRoles = async (row: User) => {
|
||||
const roles = await roleApi.getAll()
|
||||
allRoles.value = roles.map((role: Role) => ({
|
||||
key: role.id,
|
||||
label: role.name
|
||||
label: role.roleName
|
||||
}))
|
||||
selectedRoles.value = row.roles.map((roleName: string) => {
|
||||
const role = roles.find((r: Role) => r.name === roleName)
|
||||
return role ? role.id : 0
|
||||
}).filter((id: number) => id > 0)
|
||||
selectedRoles.value = row.roles || []
|
||||
roleDialogVisible.value = true
|
||||
} catch (error) {
|
||||
handleApiError(error)
|
||||
|
||||
Reference in New Issue
Block a user