jiaxing.zhou

feat(auth): 增强用户信息接口返回详细角色和权限数据

- 修改 `/api/auth/userinfo` 接口,增加返回用户真实姓名、角色列表和权限详情
- 更新 UserInfoRes DTO,添加 realName 和 roles 字段
- 在 AuthController 中实现用户详细信息查询逻辑
- 前端页面适配新的用户信息结构,展示真实姓名和角色信息
- 优化前端用户信息加载逻辑,增加加载状态提示
- 更新 API 接口文档,补充详细的响应字段说明
......@@ -103,7 +103,7 @@
**接口路径:** `GET /api/auth/userinfo`
**功能描述:** 获取当前登录用户的详细信息
**功能描述:** 获取当前登录用户的详细信息,包括用户名、真实姓名、角色列表和权限列表
**请求头:** `Authorization: Bearer {token}`
......@@ -114,11 +114,46 @@
"message": "获取用户信息成功",
"data": {
"username": "admin",
"authorities": ["ROLE_ADMIN"]
"realName": "系统管理员",
"roles": [
{
"roleId": 1,
"roleCode": "ADMIN",
"roleName": "系统管理员",
"status": 1,
"statusText": "正常",
"remark": "系统管理员角色",
"createBy": "admin",
"createTime": "2024-01-01T00:00:00",
"updateBy": "admin",
"updateTime": "2024-01-01T00:00:00"
}
],
"authorities": [
{
"authority": "ROLE_ADMIN"
}
]
}
}
```
**响应字段说明:**
- `username`: 用户名
- `realName`: 真实姓名
- `roles`: 用户角色列表
- `roleId`: 角色ID
- `roleCode`: 角色编码
- `roleName`: 角色名称
- `status`: 角色状态(0-停用/1-启用)
- `statusText`: 角色状态文本描述
- `remark`: 备注
- `createBy`: 创建者
- `createTime`: 创建时间
- `updateBy`: 更新者
- `updateTime`: 更新时间
- `authorities`: 用户权限列表(Spring Security权限对象)
## 2. 用户管理 (SysUserController)
### 2.1 获取用户列表
......
......@@ -4,6 +4,7 @@ import com.apple.erp.dto.request.LoginReq;
import com.apple.erp.dto.request.RefreshTokenReq;
import com.apple.erp.dto.response.ApiRes;
import com.apple.erp.dto.response.LoginRes;
import com.apple.erp.dto.response.RoleRes;
import com.apple.erp.dto.response.UserInfoRes;
import com.apple.erp.entity.SysUser;
import com.apple.erp.service.SysUserService;
......@@ -23,6 +24,7 @@ import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.List;
/**
* 认证控制器
......@@ -214,9 +216,34 @@ public class AuthController {
if (authentication != null && authentication.isAuthenticated()) {
String username = authentication.getName();
UserInfoRes userInfo = new UserInfoRes(username, authentication.getAuthorities());
ApiRes<UserInfoRes> response = ApiRes.success("获取用户信息成功", userInfo);
return ResponseEntity.ok(response);
try {
// 获取用户详细信息
SysUser user = sysUserService.findByUsername(username);
if (user != null) {
// 获取用户角色信息
List<RoleRes> roles = sysUserService.getUserRoles(user.getUserId());
UserInfoRes userInfo = new UserInfoRes(
user.getUsername(),
user.getRealName(),
roles,
authentication.getAuthorities()
);
ApiRes<UserInfoRes> response = ApiRes.success("获取用户信息成功", userInfo);
return ResponseEntity.ok(response);
} else {
// 如果找不到用户信息,返回基本信息
UserInfoRes userInfo = new UserInfoRes(username, authentication.getAuthorities());
ApiRes<UserInfoRes> response = ApiRes.success("获取用户信息成功", userInfo);
return ResponseEntity.ok(response);
}
} catch (Exception e) {
log.error("获取用户详细信息失败: " + e.getMessage(), e);
// 如果获取详细信息失败,返回基本信息
UserInfoRes userInfo = new UserInfoRes(username, authentication.getAuthorities());
ApiRes<UserInfoRes> response = ApiRes.success("获取用户信息成功", userInfo);
return ResponseEntity.ok(response);
}
} else {
ApiRes<UserInfoRes> response = ApiRes.error("未认证");
return ResponseEntity.status(401).body(response);
......
......@@ -5,6 +5,7 @@ import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
import java.util.List;
/**
* 用户信息响应对象
......@@ -20,6 +21,12 @@ public class UserInfoRes {
@Schema(description = "用户名", example = "admin")
private String username;
@Schema(description = "真实姓名", example = "管理员")
private String realName;
@Schema(description = "用户角色列表")
private List<RoleRes> roles;
@Schema(description = "用户权限列表")
private Collection<? extends GrantedAuthority> authorities;
......@@ -29,4 +36,11 @@ public class UserInfoRes {
this.username = username;
this.authorities = authorities;
}
public UserInfoRes(String username, String realName, List<RoleRes> roles, Collection<? extends GrantedAuthority> authorities) {
this.username = username;
this.realName = realName;
this.roles = roles;
this.authorities = authorities;
}
}
......
......@@ -72,7 +72,8 @@
<div class="header-right">
<div class="user-info">
<span class="welcome-text">欢迎,{{ userInfo?.username || '用户' }}</span>
<span class="welcome-text" v-if="userInfoLoading">加载中...</span>
<span class="welcome-text" v-else>欢迎,{{ userInfo?.username || '未知' }}</span>
<div class="user-actions">
<button class="logout-btn" @click="handleLogout">退出登录</button>
</div>
......@@ -125,6 +126,7 @@
import { ref, computed, onMounted, watch, nextTick } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { logoutApi } from '../api/auth'
import { request } from '../utils/request'
const router = useRouter()
const route = useRoute()
......@@ -134,6 +136,26 @@ const sidebarCollapsed = ref(false)
// 用户信息
const userInfo = ref<any>(null)
const userInfoLoading = ref(false)
// 获取用户信息
const fetchUserInfo = async () => {
try {
userInfoLoading.value = true
const response = await request.get('/api/auth/userinfo')
userInfo.value = response
console.log('顶部栏用户信息:', response)
} catch (error) {
console.error('获取用户信息失败:', error)
// 如果获取失败,尝试从本地存储获取
const storedUserInfo = localStorage.getItem('userInfo')
if (storedUserInfo) {
userInfo.value = JSON.parse(storedUserInfo)
}
} finally {
userInfoLoading.value = false
}
}
// 标签页容器引用
const tabContainer = ref<HTMLElement>()
......@@ -371,9 +393,17 @@ watch(() => route.path, (newPath) => {
// 组件挂载时获取用户信息
onMounted(() => {
const storedUserInfo = localStorage.getItem('userInfo')
if (storedUserInfo) {
userInfo.value = JSON.parse(storedUserInfo)
// 检查是否已登录
const token = localStorage.getItem('token')
if (token) {
// 调用API获取用户信息
fetchUserInfo()
} else {
// 如果没有token,尝试从本地存储获取
const storedUserInfo = localStorage.getItem('userInfo')
if (storedUserInfo) {
userInfo.value = JSON.parse(storedUserInfo)
}
}
})
</script>
......
......@@ -2,7 +2,8 @@
<div class="dashboard-container">
<div class="dashboard-header">
<h2>系统概览</h2>
<p>欢迎回来,{{ userInfo?.username || '用户' }}!</p>
<p v-if="loading">正在加载用户信息...</p>
<p v-else>欢迎回来,{{ userInfo?.username || '未知' }}!</p>
</div>
<div class="dashboard-stats">
......@@ -43,23 +44,25 @@
<div class="dashboard-card">
<h3>系统信息</h3>
<div class="info-grid">
<div class="info-item">
<div class="info-item">
<label>用户名:</label>
<span>{{ userInfo?.username || '未知' }}</span>
</div>
<div class="info-item">
<span v-if="loading">加载中...</span>
<span v-else>{{ userInfo?.username || '未知' }}</span>
</div>
<div class="info-item">
<label>角色:</label>
<span>{{ userInfo?.roles?.[0]?.roleName || '普通用户' }}</span>
</div>
<div class="info-item">
<span v-if="loading">加载中...</span>
<span v-else>{{ getRoleName(userInfo) || '普通用户' }}</span>
</div>
<div class="info-item">
<label>登录时间:</label>
<span>{{ currentTime }}</span>
</div>
<div class="info-item">
</div>
<div class="info-item">
<label>系统版本:</label>
<span>v1.0.0</span>
</div>
</div>
</div>
</div>
<div class="dashboard-card">
......@@ -78,26 +81,76 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { request } from '@/utils/request'
const router = useRouter()
const currentTime = ref('')
const userInfo = ref<any>(null)
const loading = ref(false)
// 获取角色名称
const getRoleName = (userInfo: any) => {
// 优先使用roles字段中的角色信息
if (userInfo?.roles && Array.isArray(userInfo.roles) && userInfo.roles.length > 0) {
return userInfo.roles[0].roleName || '普通用户'
}
// 如果没有roles字段,回退到authorities
const authorities = userInfo?.authorities
if (authorities && Array.isArray(authorities)) {
// 查找ROLE_开头的权限
const roleAuthority = authorities.find(auth =>
auth.authority && auth.authority.startsWith('ROLE_')
)
if (roleAuthority) {
// 移除ROLE_前缀并转换为中文
const roleName = roleAuthority.authority.replace('ROLE_', '')
const roleMap: { [key: string]: string } = {
'ADMIN': '管理员',
'USER': '普通用户',
'MANAGER': '经理',
'OPERATOR': '操作员'
}
return roleMap[roleName] || roleName
}
}
return '普通用户'
}
// 获取用户信息
const fetchUserInfo = async () => {
try {
loading.value = true
const response = await request.get('/api/auth/userinfo')
userInfo.value = response
console.log('用户信息:', response)
} catch (error) {
console.error('获取用户信息失败:', error)
// 如果获取失败,尝试从本地存储获取
const storedUserInfo = localStorage.getItem('userInfo')
if (storedUserInfo) {
userInfo.value = JSON.parse(storedUserInfo)
}
} finally {
loading.value = false
}
}
onMounted(() => {
// 获取当前时间
currentTime.value = new Date().toLocaleString()
// 获取用户信息
const storedUserInfo = localStorage.getItem('userInfo')
if (storedUserInfo) {
userInfo.value = JSON.parse(storedUserInfo)
}
// 检查是否已登录
const token = localStorage.getItem('token')
if (!token) {
router.push('/')
return
}
// 获取用户信息
fetchUserInfo()
})
const logout = () => {
......