jiaxing.zhou

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

- 修改 `/api/auth/userinfo` 接口,增加返回用户真实姓名、角色列表和权限详情
- 更新 UserInfoRes DTO,添加 realName 和 roles 字段
- 在 AuthController 中实现用户详细信息查询逻辑
- 前端页面适配新的用户信息结构,展示真实姓名和角色信息
- 优化前端用户信息加载逻辑,增加加载状态提示
- 更新 API 接口文档,补充详细的响应字段说明
...@@ -103,7 +103,7 @@ ...@@ -103,7 +103,7 @@
103 103
104 **接口路径:** `GET /api/auth/userinfo` 104 **接口路径:** `GET /api/auth/userinfo`
105 105
106 -**功能描述:** 获取当前登录用户的详细信息 106 +**功能描述:** 获取当前登录用户的详细信息,包括用户名、真实姓名、角色列表和权限列表
107 107
108 **请求头:** `Authorization: Bearer {token}` 108 **请求头:** `Authorization: Bearer {token}`
109 109
...@@ -114,11 +114,46 @@ ...@@ -114,11 +114,46 @@
114 "message": "获取用户信息成功", 114 "message": "获取用户信息成功",
115 "data": { 115 "data": {
116 "username": "admin", 116 "username": "admin",
117 - "authorities": ["ROLE_ADMIN"] 117 + "realName": "系统管理员",
118 + "roles": [
119 + {
120 + "roleId": 1,
121 + "roleCode": "ADMIN",
122 + "roleName": "系统管理员",
123 + "status": 1,
124 + "statusText": "正常",
125 + "remark": "系统管理员角色",
126 + "createBy": "admin",
127 + "createTime": "2024-01-01T00:00:00",
128 + "updateBy": "admin",
129 + "updateTime": "2024-01-01T00:00:00"
130 + }
131 + ],
132 + "authorities": [
133 + {
134 + "authority": "ROLE_ADMIN"
135 + }
136 + ]
118 } 137 }
119 } 138 }
120 ``` 139 ```
121 140
141 +**响应字段说明:**
142 +- `username`: 用户名
143 +- `realName`: 真实姓名
144 +- `roles`: 用户角色列表
145 + - `roleId`: 角色ID
146 + - `roleCode`: 角色编码
147 + - `roleName`: 角色名称
148 + - `status`: 角色状态(0-停用/1-启用)
149 + - `statusText`: 角色状态文本描述
150 + - `remark`: 备注
151 + - `createBy`: 创建者
152 + - `createTime`: 创建时间
153 + - `updateBy`: 更新者
154 + - `updateTime`: 更新时间
155 +- `authorities`: 用户权限列表(Spring Security权限对象)
156 +
122 ## 2. 用户管理 (SysUserController) 157 ## 2. 用户管理 (SysUserController)
123 158
124 ### 2.1 获取用户列表 159 ### 2.1 获取用户列表
......
...@@ -4,6 +4,7 @@ import com.apple.erp.dto.request.LoginReq; ...@@ -4,6 +4,7 @@ import com.apple.erp.dto.request.LoginReq;
4 import com.apple.erp.dto.request.RefreshTokenReq; 4 import com.apple.erp.dto.request.RefreshTokenReq;
5 import com.apple.erp.dto.response.ApiRes; 5 import com.apple.erp.dto.response.ApiRes;
6 import com.apple.erp.dto.response.LoginRes; 6 import com.apple.erp.dto.response.LoginRes;
7 +import com.apple.erp.dto.response.RoleRes;
7 import com.apple.erp.dto.response.UserInfoRes; 8 import com.apple.erp.dto.response.UserInfoRes;
8 import com.apple.erp.entity.SysUser; 9 import com.apple.erp.entity.SysUser;
9 import com.apple.erp.service.SysUserService; 10 import com.apple.erp.service.SysUserService;
...@@ -23,6 +24,7 @@ import org.springframework.web.bind.annotation.*; ...@@ -23,6 +24,7 @@ import org.springframework.web.bind.annotation.*;
23 24
24 import javax.servlet.http.HttpServletRequest; 25 import javax.servlet.http.HttpServletRequest;
25 import javax.validation.Valid; 26 import javax.validation.Valid;
27 +import java.util.List;
26 28
27 /** 29 /**
28 * 认证控制器 30 * 认证控制器
...@@ -214,9 +216,34 @@ public class AuthController { ...@@ -214,9 +216,34 @@ public class AuthController {
214 if (authentication != null && authentication.isAuthenticated()) { 216 if (authentication != null && authentication.isAuthenticated()) {
215 String username = authentication.getName(); 217 String username = authentication.getName();
216 218
217 - UserInfoRes userInfo = new UserInfoRes(username, authentication.getAuthorities()); 219 + try {
218 - ApiRes<UserInfoRes> response = ApiRes.success("获取用户信息成功", userInfo); 220 + // 获取用户详细信息
219 - return ResponseEntity.ok(response); 221 + SysUser user = sysUserService.findByUsername(username);
222 + if (user != null) {
223 + // 获取用户角色信息
224 + List<RoleRes> roles = sysUserService.getUserRoles(user.getUserId());
225 +
226 + UserInfoRes userInfo = new UserInfoRes(
227 + user.getUsername(),
228 + user.getRealName(),
229 + roles,
230 + authentication.getAuthorities()
231 + );
232 + ApiRes<UserInfoRes> response = ApiRes.success("获取用户信息成功", userInfo);
233 + return ResponseEntity.ok(response);
234 + } else {
235 + // 如果找不到用户信息,返回基本信息
236 + UserInfoRes userInfo = new UserInfoRes(username, authentication.getAuthorities());
237 + ApiRes<UserInfoRes> response = ApiRes.success("获取用户信息成功", userInfo);
238 + return ResponseEntity.ok(response);
239 + }
240 + } catch (Exception e) {
241 + log.error("获取用户详细信息失败: " + e.getMessage(), e);
242 + // 如果获取详细信息失败,返回基本信息
243 + UserInfoRes userInfo = new UserInfoRes(username, authentication.getAuthorities());
244 + ApiRes<UserInfoRes> response = ApiRes.success("获取用户信息成功", userInfo);
245 + return ResponseEntity.ok(response);
246 + }
220 } else { 247 } else {
221 ApiRes<UserInfoRes> response = ApiRes.error("未认证"); 248 ApiRes<UserInfoRes> response = ApiRes.error("未认证");
222 return ResponseEntity.status(401).body(response); 249 return ResponseEntity.status(401).body(response);
......
...@@ -5,6 +5,7 @@ import lombok.Data; ...@@ -5,6 +5,7 @@ import lombok.Data;
5 import org.springframework.security.core.GrantedAuthority; 5 import org.springframework.security.core.GrantedAuthority;
6 6
7 import java.util.Collection; 7 import java.util.Collection;
8 +import java.util.List;
8 9
9 /** 10 /**
10 * 用户信息响应对象 11 * 用户信息响应对象
...@@ -20,6 +21,12 @@ public class UserInfoRes { ...@@ -20,6 +21,12 @@ public class UserInfoRes {
20 @Schema(description = "用户名", example = "admin") 21 @Schema(description = "用户名", example = "admin")
21 private String username; 22 private String username;
22 23
24 + @Schema(description = "真实姓名", example = "管理员")
25 + private String realName;
26 +
27 + @Schema(description = "用户角色列表")
28 + private List<RoleRes> roles;
29 +
23 @Schema(description = "用户权限列表") 30 @Schema(description = "用户权限列表")
24 private Collection<? extends GrantedAuthority> authorities; 31 private Collection<? extends GrantedAuthority> authorities;
25 32
...@@ -29,4 +36,11 @@ public class UserInfoRes { ...@@ -29,4 +36,11 @@ public class UserInfoRes {
29 this.username = username; 36 this.username = username;
30 this.authorities = authorities; 37 this.authorities = authorities;
31 } 38 }
39 +
40 + public UserInfoRes(String username, String realName, List<RoleRes> roles, Collection<? extends GrantedAuthority> authorities) {
41 + this.username = username;
42 + this.realName = realName;
43 + this.roles = roles;
44 + this.authorities = authorities;
45 + }
32 } 46 }
......
...@@ -72,7 +72,8 @@ ...@@ -72,7 +72,8 @@
72 72
73 <div class="header-right"> 73 <div class="header-right">
74 <div class="user-info"> 74 <div class="user-info">
75 - <span class="welcome-text">欢迎,{{ userInfo?.username || '用户' }}</span> 75 + <span class="welcome-text" v-if="userInfoLoading">加载中...</span>
76 + <span class="welcome-text" v-else>欢迎,{{ userInfo?.username || '未知' }}</span>
76 <div class="user-actions"> 77 <div class="user-actions">
77 <button class="logout-btn" @click="handleLogout">退出登录</button> 78 <button class="logout-btn" @click="handleLogout">退出登录</button>
78 </div> 79 </div>
...@@ -125,6 +126,7 @@ ...@@ -125,6 +126,7 @@
125 import { ref, computed, onMounted, watch, nextTick } from 'vue' 126 import { ref, computed, onMounted, watch, nextTick } from 'vue'
126 import { useRouter, useRoute } from 'vue-router' 127 import { useRouter, useRoute } from 'vue-router'
127 import { logoutApi } from '../api/auth' 128 import { logoutApi } from '../api/auth'
129 +import { request } from '../utils/request'
128 130
129 const router = useRouter() 131 const router = useRouter()
130 const route = useRoute() 132 const route = useRoute()
...@@ -134,6 +136,26 @@ const sidebarCollapsed = ref(false) ...@@ -134,6 +136,26 @@ const sidebarCollapsed = ref(false)
134 136
135 // 用户信息 137 // 用户信息
136 const userInfo = ref<any>(null) 138 const userInfo = ref<any>(null)
139 +const userInfoLoading = ref(false)
140 +
141 +// 获取用户信息
142 +const fetchUserInfo = async () => {
143 + try {
144 + userInfoLoading.value = true
145 + const response = await request.get('/api/auth/userinfo')
146 + userInfo.value = response
147 + console.log('顶部栏用户信息:', response)
148 + } catch (error) {
149 + console.error('获取用户信息失败:', error)
150 + // 如果获取失败,尝试从本地存储获取
151 + const storedUserInfo = localStorage.getItem('userInfo')
152 + if (storedUserInfo) {
153 + userInfo.value = JSON.parse(storedUserInfo)
154 + }
155 + } finally {
156 + userInfoLoading.value = false
157 + }
158 +}
137 159
138 // 标签页容器引用 160 // 标签页容器引用
139 const tabContainer = ref<HTMLElement>() 161 const tabContainer = ref<HTMLElement>()
...@@ -371,9 +393,17 @@ watch(() => route.path, (newPath) => { ...@@ -371,9 +393,17 @@ watch(() => route.path, (newPath) => {
371 393
372 // 组件挂载时获取用户信息 394 // 组件挂载时获取用户信息
373 onMounted(() => { 395 onMounted(() => {
374 - const storedUserInfo = localStorage.getItem('userInfo') 396 + // 检查是否已登录
375 - if (storedUserInfo) { 397 + const token = localStorage.getItem('token')
376 - userInfo.value = JSON.parse(storedUserInfo) 398 + if (token) {
399 + // 调用API获取用户信息
400 + fetchUserInfo()
401 + } else {
402 + // 如果没有token,尝试从本地存储获取
403 + const storedUserInfo = localStorage.getItem('userInfo')
404 + if (storedUserInfo) {
405 + userInfo.value = JSON.parse(storedUserInfo)
406 + }
377 } 407 }
378 }) 408 })
379 </script> 409 </script>
......
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
2 <div class="dashboard-container"> 2 <div class="dashboard-container">
3 <div class="dashboard-header"> 3 <div class="dashboard-header">
4 <h2>系统概览</h2> 4 <h2>系统概览</h2>
5 - <p>欢迎回来,{{ userInfo?.username || '用户' }}!</p> 5 + <p v-if="loading">正在加载用户信息...</p>
6 + <p v-else>欢迎回来,{{ userInfo?.username || '未知' }}!</p>
6 </div> 7 </div>
7 8
8 <div class="dashboard-stats"> 9 <div class="dashboard-stats">
...@@ -43,23 +44,25 @@ ...@@ -43,23 +44,25 @@
43 <div class="dashboard-card"> 44 <div class="dashboard-card">
44 <h3>系统信息</h3> 45 <h3>系统信息</h3>
45 <div class="info-grid"> 46 <div class="info-grid">
46 - <div class="info-item"> 47 + <div class="info-item">
47 <label>用户名:</label> 48 <label>用户名:</label>
48 - <span>{{ userInfo?.username || '未知' }}</span> 49 + <span v-if="loading">加载中...</span>
49 - </div> 50 + <span v-else>{{ userInfo?.username || '未知' }}</span>
50 - <div class="info-item"> 51 + </div>
52 + <div class="info-item">
51 <label>角色:</label> 53 <label>角色:</label>
52 - <span>{{ userInfo?.roles?.[0]?.roleName || '普通用户' }}</span> 54 + <span v-if="loading">加载中...</span>
53 - </div> 55 + <span v-else>{{ getRoleName(userInfo) || '普通用户' }}</span>
54 - <div class="info-item"> 56 + </div>
57 + <div class="info-item">
55 <label>登录时间:</label> 58 <label>登录时间:</label>
56 <span>{{ currentTime }}</span> 59 <span>{{ currentTime }}</span>
57 - </div> 60 + </div>
58 - <div class="info-item"> 61 + <div class="info-item">
59 <label>系统版本:</label> 62 <label>系统版本:</label>
60 <span>v1.0.0</span> 63 <span>v1.0.0</span>
61 </div> 64 </div>
62 - </div> 65 + </div>
63 </div> 66 </div>
64 67
65 <div class="dashboard-card"> 68 <div class="dashboard-card">
...@@ -78,26 +81,76 @@ ...@@ -78,26 +81,76 @@
78 <script setup lang="ts"> 81 <script setup lang="ts">
79 import { ref, onMounted } from 'vue' 82 import { ref, onMounted } from 'vue'
80 import { useRouter } from 'vue-router' 83 import { useRouter } from 'vue-router'
84 +import { request } from '@/utils/request'
81 85
82 const router = useRouter() 86 const router = useRouter()
83 const currentTime = ref('') 87 const currentTime = ref('')
84 const userInfo = ref<any>(null) 88 const userInfo = ref<any>(null)
89 +const loading = ref(false)
90 +
91 +// 获取角色名称
92 +const getRoleName = (userInfo: any) => {
93 + // 优先使用roles字段中的角色信息
94 + if (userInfo?.roles && Array.isArray(userInfo.roles) && userInfo.roles.length > 0) {
95 + return userInfo.roles[0].roleName || '普通用户'
96 + }
97 +
98 + // 如果没有roles字段,回退到authorities
99 + const authorities = userInfo?.authorities
100 + if (authorities && Array.isArray(authorities)) {
101 + // 查找ROLE_开头的权限
102 + const roleAuthority = authorities.find(auth =>
103 + auth.authority && auth.authority.startsWith('ROLE_')
104 + )
105 +
106 + if (roleAuthority) {
107 + // 移除ROLE_前缀并转换为中文
108 + const roleName = roleAuthority.authority.replace('ROLE_', '')
109 + const roleMap: { [key: string]: string } = {
110 + 'ADMIN': '管理员',
111 + 'USER': '普通用户',
112 + 'MANAGER': '经理',
113 + 'OPERATOR': '操作员'
114 + }
115 + return roleMap[roleName] || roleName
116 + }
117 + }
118 +
119 + return '普通用户'
120 +}
121 +
122 +// 获取用户信息
123 +const fetchUserInfo = async () => {
124 + try {
125 + loading.value = true
126 + const response = await request.get('/api/auth/userinfo')
127 + userInfo.value = response
128 + console.log('用户信息:', response)
129 + } catch (error) {
130 + console.error('获取用户信息失败:', error)
131 + // 如果获取失败,尝试从本地存储获取
132 + const storedUserInfo = localStorage.getItem('userInfo')
133 + if (storedUserInfo) {
134 + userInfo.value = JSON.parse(storedUserInfo)
135 + }
136 + } finally {
137 + loading.value = false
138 + }
139 +}
85 140
86 onMounted(() => { 141 onMounted(() => {
87 // 获取当前时间 142 // 获取当前时间
88 currentTime.value = new Date().toLocaleString() 143 currentTime.value = new Date().toLocaleString()
89 144
90 - // 获取用户信息
91 - const storedUserInfo = localStorage.getItem('userInfo')
92 - if (storedUserInfo) {
93 - userInfo.value = JSON.parse(storedUserInfo)
94 - }
95 -
96 // 检查是否已登录 145 // 检查是否已登录
97 const token = localStorage.getItem('token') 146 const token = localStorage.getItem('token')
98 if (!token) { 147 if (!token) {
99 router.push('/') 148 router.push('/')
149 + return
100 } 150 }
151 +
152 + // 获取用户信息
153 + fetchUserInfo()
101 }) 154 })
102 155
103 const logout = () => { 156 const logout = () => {
......