登录和列表

This commit is contained in:
黑小马 2025-07-25 13:57:36 +08:00
parent 90d40386ac
commit 4946911f1c
11 changed files with 214 additions and 191 deletions

View File

@ -0,0 +1,17 @@
package com.lktx.center.config;
import cn.dev33.satoken.exception.NotLoginException;
import cn.hserver.core.ioc.annotation.Bean;
import cn.hserver.core.server.util.JsonResult;
import cn.hserver.plugin.web.context.Webkit;
import cn.hserver.plugin.web.interfaces.GlobalException;
@Bean
public class AllException implements GlobalException {
@Override
public void handler(Throwable throwable, int httpStatusCode, String errorDescription, Webkit webkit) {
if (throwable.getCause() instanceof NotLoginException){
webkit.httpResponse.sendJson(JsonResult.error(-2, errorDescription));
}
}
}

View File

@ -0,0 +1,27 @@
package com.lktx.center.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.hserver.core.ioc.annotation.Autowired;
import cn.hserver.core.server.util.JsonResult;
import cn.hserver.plugin.web.annotation.Controller;
import cn.hserver.plugin.web.annotation.GET;
import com.lktx.center.service.AppCenterService;
import java.util.Map;
@Controller("/app-center")
public class AppCenterController {
@Autowired
private AppCenterService appCenterService;
@GET("/list")
@SaCheckLogin
public JsonResult list(){
Map<String, Object> appList = appCenterService.getAppList();
if (appList != null) {
return JsonResult.ok().put("data", appList);
}
return JsonResult.error();
}
}

View File

@ -52,19 +52,4 @@ public class HomeController {
}
@GET("/logout")
public void logout(HttpResponse response) {
if (StpUtil.isLogin()){
//可以全局退出
SaSession session = StpUtil.getSession();
AuthToken authToken = session.get(Data.AuthToken,null);
if (authToken != null){
AuthResponse revoke = authRequest.revoke(authToken);
System.out.println(revoke.getMsg());
}
//子系统退出
StpUtil.logout();
}
response.redirect("/");
}
}

View File

@ -5,11 +5,13 @@ import cn.dev33.satoken.stp.StpUtil;
import cn.hserver.core.ioc.annotation.Autowired;
import cn.hserver.core.server.util.JsonResult;
import cn.hserver.plugin.web.annotation.Controller;
import cn.hserver.plugin.web.annotation.GET;
import cn.hserver.plugin.web.annotation.RequestMapping;
import cn.hserver.plugin.web.interfaces.HttpRequest;
import cn.hserver.plugin.web.interfaces.HttpResponse;
import com.lktx.center.config.Data;
import com.lktx.center.config.SsoAuthRequest;
import com.lktx.center.domain.vo.LoginInfo;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
@ -41,10 +43,36 @@ public class RestAuthController {
StpUtil.login(login.getData().getUuid());
SaSession session = StpUtil.getSession();
session.set(Data.AuthToken, token);
return JsonResult.ok().put("data", data);
LoginInfo build = LoginInfo.builder()
.userId(data.getUuid())
.avatar(data.getAvatar())
.username(data.getUsername())
.nickname(data.getNickname())
.token(StpUtil.getTokenInfo().tokenValue)
.build();
return JsonResult.ok().put("data", build);
}catch (Exception e) {
log.error("login error",e);
}
return JsonResult.error();
}
@GET("/logout")
public JsonResult logout() {
System.out.println(StpUtil.getSession().getId());
if (StpUtil.isLogin()){
SaSession session = StpUtil.getSession();
AuthToken authToken = session.get(Data.AuthToken,null);
if (authToken != null){
AuthResponse<Void> revoke = authRequest.revoke(authToken);
System.out.println(revoke.getMsg());
}
//子系统退出
StpUtil.logout();
}
return JsonResult.ok();
}
}

View File

@ -0,0 +1,14 @@
package com.lktx.center.domain.vo;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class LoginInfo {
private String userId;
private String nickname;
private String username;
private String avatar;
private String token;
}

View File

@ -0,0 +1,58 @@
package com.lktx.center.service;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hserver.core.ioc.annotation.Autowired;
import cn.hserver.core.ioc.annotation.Bean;
import cn.hserver.core.server.util.JsonResult;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.lktx.center.config.Data;
import com.lktx.center.config.SsoAuthRequest;
import com.lktx.center.domain.bean.SsoApp;
import com.lktx.center.domain.vo.SsoUserAppVO;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Bean
public class AppCenterService {
@Autowired
private SsoAuthRequest ssoAuthRequest;
private final Cache<String, Map<String,Object>> expiringCache = CacheBuilder.newBuilder()
.expireAfterWrite(30, TimeUnit.MINUTES) // 写入后30分钟过期
.maximumSize(1)
.build();
public Map<String,Object> getAppList(){
Map<String, Object> appList = expiringCache.getIfPresent("appList");
if(appList != null){
return appList;
}
SaSession session = StpUtil.getSession();
AuthToken authToken = session.get(Data.AuthToken,null);
if (authToken != null){
AuthResponse<SsoUserAppVO> center = ssoAuthRequest.center(authToken);
if (center.ok()) {
Map<String, Object> data = Map.of(
"user", center.getData().getSsoUser(),
"appList", center.getData().getSsoAppList(),
"appGroup", center.getData().getSsoAppList().stream().map(SsoApp::getSsoAppGroup).collect(Collectors.toSet())
);
expiringCache.put("appList", data);
return data;
}
}
return null;
}
}

8
web/src/api/appcenter.js Normal file
View File

@ -0,0 +1,8 @@
import http from '../data/http'
export function appCenterList() {
return http({
url: '/app-center/list',
method: 'get',
})
}

View File

@ -7,3 +7,9 @@ export function login(data) {
data
})
}
export function logout() {
return http({
url: '/oauth/logout',
method: 'get',
})
}

View File

@ -21,7 +21,7 @@ service.interceptors.request.use(
// let each request carry token
// ['X-Token'] is a custom headers key
// please modify it according to the actual situation
config.headers['token'] = userInfo.getUserInfo().token
config.headers['satoken'] = userInfo.getUserInfo().token
}
return config
},
@ -49,8 +49,9 @@ service.interceptors.response.use(
// if the custom code is not 20000, it is judged as an error.
if (res.code !== 200) {
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === -2 || res.code === -3 || res.code === -4 || res.code === -5) {
if (res.code === -2) {
location.href = "/login"
return null
}
return res
} else {

View File

@ -76,7 +76,7 @@
<div class="flex flex-col items-center p-4">
<div class="w-14 h-14 rounded-lg bg-primary/10 flex items-center justify-center mb-3">
<el-icon :size="24" :class="element.icon"></el-icon>
<el-avatar :src="element.icon" />
</div>
<h3 class="font-medium text-gray-800 mb-1 text-center truncate w-full">{{ element.name }}</h3>
<p class="text-xs text-gray-500 text-center truncate w-full">{{ element.description }}</p>
@ -123,7 +123,7 @@
>
<div class="flex flex-col items-center p-4">
<div class="w-14 h-14 rounded-lg bg-primary/10 flex items-center justify-center mb-3">
<el-icon :size="24" :class="app.icon"></el-icon>
<el-avatar :src="app.icon" />
</div>
<h3 class="font-medium text-gray-800 mb-1 text-center truncate w-full">{{ app.name }}</h3>
<p class="text-xs text-gray-500 text-center truncate w-full">{{ app.description }}</p>
@ -181,7 +181,7 @@
:label="app.id"
class="flex items-center"
>
<el-icon :class="app.icon" class="mr-2"></el-icon>
<el-avatar :src="app.icon" />
<span class="text-sm">{{ app.name }}</span>
</el-checkbox>
</el-checkbox-group>
@ -212,6 +212,7 @@
<script lang="ts" setup>
import { ref, computed, onMounted } from 'vue'
import {appCenterList} from '../api/appcenter'
import draggable from 'vuedraggable'
import {
Collection,
@ -240,11 +241,6 @@ import { ElMessage, ElDialog } from 'element-plus'
//
const categories = ref([
{ id: 'workflow', name: '工作流程' },
{ id: 'data', name: '数据分析' },
{ id: 'communication', name: '沟通协作' },
{ id: 'document', name: '文档管理' },
{ id: 'system', name: '系统工具' }
])
//
@ -261,166 +257,7 @@ const drag=ref(false)
//
const allApps = ref<App[]>([
{
id: 1,
name: '项目管理系统',
description: '管理项目进度、任务分配和资源',
icon: Monitor,
category: 'workflow',
url: '/project-management'
},
{
id: 2,
name: '文档中心',
description: '企业级文档管理和协作平台',
icon: Document,
category: 'document',
url: '/document-center'
},
{
id: 3,
name: '企业邮箱',
description: '安全可靠的企业邮件系统',
icon: Message,
category: 'communication',
url: '/mail'
},
{
id: 4,
name: '人力资源系统',
description: '员工信息管理和人事流程',
icon: User,
category: 'workflow',
url: '/hr-system'
},
{
id: 5,
name: '数据分析平台',
description: '企业数据可视化和分析工具',
icon: PieChart,
category: 'data',
url: '/data-analysis'
},
{
id: 6,
name: '日程安排',
description: '团队和个人日程管理',
icon: Calendar,
category: 'workflow',
url: '/calendar'
},
{
id: 7,
name: '知识库',
description: '企业知识沉淀和共享平台',
icon: Folder,
category: 'document',
url: '/knowledge-base'
},
{
id: 8,
name: '视频会议',
description: '高清视频会议和远程协作',
icon: PieChart,
category: 'communication',
url: '/video-conference'
},
{
id: 9,
name: '系统设置',
description: '企业系统配置和管理',
icon: Setting,
category: 'system',
url: '/system-settings'
},
{
id: 10,
name: '数据备份',
description: '企业数据定期备份和恢复',
icon: Download,
category: 'system',
url: '/data-backup'
},
{
id: 11,
name: '报表生成器',
description: '自定义报表和数据导出',
icon: PieChart,
category: 'data',
url: '/report-generator'
},
{
id: 12,
name: '移动办公',
description: '随时随地处理工作事务',
icon: PieChart,
category: 'workflow',
url: '/mobile-office'
},
{
id: 13,
name: '文件传输',
description: '安全高效的大文件传输工具',
icon: Upload,
category: 'system',
url: '/file-transfer'
},
{
id: 14,
name: '工单系统',
description: 'IT服务请求和问题跟踪',
icon: PieChart,
category: 'workflow',
url: '/work-order'
},
{
id: 15,
name: '实时监控',
description: '系统性能和状态实时监控',
icon: Clock,
category: 'system',
url: '/monitoring'
},
{
id: 16,
name: '企业公告',
description: '公司通知和重要消息发布',
icon: Bell,
category: 'communication',
url: '/announcements'
},
{
id: 17,
name: '权限管理',
description: '用户权限和访问控制',
icon: Lock,
category: 'system',
url: '/permission-management'
},
{
id: 18,
name: '数据同步',
description: '多系统间数据实时同步',
icon: PieChart,
category: 'data',
url: '/data-sync'
},
{
id: 19,
name: '协作白板',
description: '团队在线协作和头脑风暴',
icon: Share,
category: 'communication',
url: '/collaboration-board'
},
{
id: 20,
name: '应用商店',
description: '发现和安装企业应用',
icon: Star,
category: 'system',
url: '/app-store'
}
])
// ID
@ -608,8 +445,35 @@ const loadMyAppsFromLocalStorage = () => {
selectedApps.value = [...myAppIds.value]
}
const loadAppList = () => {
appCenterList().then(res=>{
if (res.code===200){
categories.value = res.data.appGroup.map(item=>({
id: item.ssoAppGroupId,
name: item.name
}))
allApps.value = res.data.appList.map(item=>({
id: item.ssoAppId,
name: item.appName,
description: item.remark,
icon: item.appIcon,
category: item.ssoAppGroupId,
url: item.url
}))
console.log(res)
// allApps.value = res.data
}
})
}
//
onMounted(() => {
loadAppList()
loadMyAppsFromLocalStorage()
})
</script>

View File

@ -39,8 +39,14 @@
<div class="flex items-center">
<img :src="userInfoData?.avatar" alt="User Avatar" class="w-8 h-8 rounded-full object-cover">
<span class="ml-2 hidden sm:block">{{ userInfoData?.nickname }}</span>
<el-icon><CaretBottom /></el-icon>
<el-dropdown trigger="click">
<span class="el-dropdown-link text-white cursor-pointer ml-2">{{ userInfoData?.nickname }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="handelLogout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</header>
@ -117,7 +123,7 @@
</div>
</template>
<script lang="ts" setup>
<script setup>
import {onMounted, ref} from 'vue'
import {
Document,
@ -140,16 +146,17 @@ import {
import userInfo from '../../data/userInfo.js'
import {useRouter} from "vue-router";
import { logout } from '../../api/login';
const router=useRouter();
const userInfoData=ref(null);
const isCollapse = ref(false)
const handleOpen = (key: string, keyPath: string[]) => {
const handleOpen = (key, keyPath) => {
console.log(key, keyPath)
}
const handleClose = (key: string, keyPath: string[]) => {
const handleClose = (key, keyPath) => {
console.log(key, keyPath)
}
@ -163,6 +170,14 @@ onMounted(()=>{
})
const handelLogout = () => {
logout().then(res=>{
if (res.code===200){
userInfo.removeUserInfo()
location.reload()
}
})
}
</script>
<style scoped>