From ff45f6dcb85102ae1a0545a126ddb665a5296503 Mon Sep 17 00:00:00 2001 From: zhu-mingye <934230207@qq.com> Date: Mon, 20 Feb 2023 23:25:36 +0800 Subject: [PATCH] [Refactor][admin] Refactor the login process (#1661) * Refactor the login process * modify UserServiceImpl.java * modify fullScreen * delete console.log && modify i18n * spotless apply code * delete fullscreen style * modify access.ts * Optimize login process * format web code * added readme content * modify github repo * modify TenantContextHolder.java --- .../java/org/dinky/configure/AppConfig.java | 3 +- .../dinky/context/UserInfoContextHolder.java | 43 +++ .../org/dinky/controller/AdminController.java | 18 +- .../java/org/dinky/service/UserService.java | 17 +- .../dinky/service/impl/UserServiceImpl.java | 104 +++---- dinky-web/README.md | 11 +- dinky-web/README_zh_CN.md | 8 +- dinky-web/config/config.ts | 8 +- dinky-web/config/defaultSettings.ts | 4 +- dinky-web/config/proxy.ts | 3 +- dinky-web/config/routes.ts | 10 +- dinky-web/jest.config.ts | 2 +- dinky-web/mock/listTableList.ts | 4 +- dinky-web/mock/user.ts | 2 +- dinky-web/package.json | 3 +- dinky-web/src/access.ts | 2 +- dinky-web/src/app.tsx | 44 +-- dinky-web/src/components/Footer/index.tsx | 14 +- .../src/components/HeaderDropdown/index.tsx | 8 +- .../components/PasswordForm/PasswordForm.tsx | 32 +- .../RightContent/AvatarDropdown.tsx | 30 +- .../src/components/RightContent/index.tsx | 63 ++-- dinky-web/src/global.less | 3 + dinky-web/src/global.tsx | 10 +- dinky-web/src/locales/en-US.ts | 2 +- dinky-web/src/locales/en-US/global.ts | 3 + dinky-web/src/locales/en-US/pages.ts | 11 +- dinky-web/src/locales/zh-CN.ts | 2 +- dinky-web/src/locales/zh-CN/global.ts | 3 + dinky-web/src/locales/zh-CN/pages.ts | 8 +- .../src/pages/{404.tsx => 404/index.tsx} | 4 +- .../src/pages/{About.tsx => About/index.tsx} | 97 ++++-- dinky-web/src/pages/User/Login/index.tsx | 141 ++++++--- dinky-web/src/requestErrorConfig.ts | 8 +- dinky-web/src/services/api.ts | 70 +---- dinky-web/src/services/index.ts | 4 - dinky-web/src/services/typings.d.ts | 105 +------ dinky-web/src/types/User/data.d.ts | 122 ++++---- dinky-web/src/utils/intl.ts | 7 +- dinky-web/types/cache/mock/mock.cache.js | 1 - dlink-web/config/proxy.ts | 2 +- dlink-web/config/routes.ts | 13 +- dlink-web/src/access.ts | 2 +- dlink-web/src/app.tsx | 280 +++++++++++------- .../RightContent/AvatarDropdown.tsx | 8 +- dlink-web/src/locales/en-US/pages.ts | 6 +- dlink-web/src/locales/zh-CN/pages.ts | 11 +- dlink-web/src/pages/user/Login/index.tsx | 272 ++++++++--------- dlink-web/src/services/ant-design-pro/api.ts | 31 +- .../src/services/ant-design-pro/typings.d.ts | 30 +- 50 files changed, 880 insertions(+), 809 deletions(-) create mode 100644 dinky-admin/src/main/java/org/dinky/context/UserInfoContextHolder.java rename dinky-web/src/pages/{404.tsx => 404/index.tsx} (93%) rename dinky-web/src/pages/{About.tsx => About/index.tsx} (51%) diff --git a/dinky-admin/src/main/java/org/dinky/configure/AppConfig.java b/dinky-admin/src/main/java/org/dinky/configure/AppConfig.java index c44cd45986..15eb367c85 100644 --- a/dinky-admin/src/main/java/org/dinky/configure/AppConfig.java +++ b/dinky-admin/src/main/java/org/dinky/configure/AppConfig.java @@ -77,10 +77,11 @@ public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new SaInterceptor()) .addPathPatterns("/api/**") .excludePathPatterns("/api/login") - .excludePathPatterns("/api/geTenants") .excludePathPatterns("/openapi/**"); registry.addInterceptor(new TenantInterceptor()) + .addPathPatterns("/api/**") + .excludePathPatterns("/api/login") .addPathPatterns("/api/alertGroup/**") .addPathPatterns("/api/alertHistory/**") .addPathPatterns("/api/alertInstance/**") diff --git a/dinky-admin/src/main/java/org/dinky/context/UserInfoContextHolder.java b/dinky-admin/src/main/java/org/dinky/context/UserInfoContextHolder.java new file mode 100644 index 0000000000..815cd0cd07 --- /dev/null +++ b/dinky-admin/src/main/java/org/dinky/context/UserInfoContextHolder.java @@ -0,0 +1,43 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.dinky.context; + +import org.dinky.dto.UserDTO; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** UserInfoContextHolder */ +public class UserInfoContextHolder { + + private static final Map USER_INFO = new ConcurrentHashMap<>(); + + public static void set(Integer userId, UserDTO userInfo) { + USER_INFO.put(userId, userInfo); + } + + public static UserDTO get(Integer userId) { + return USER_INFO.get(userId); + } + + public static void refresh(Integer userId, UserDTO userInfo) { + set(userId, userInfo); + } +} diff --git a/dinky-admin/src/main/java/org/dinky/controller/AdminController.java b/dinky-admin/src/main/java/org/dinky/controller/AdminController.java index e2b973441f..a654835a9c 100644 --- a/dinky-admin/src/main/java/org/dinky/controller/AdminController.java +++ b/dinky-admin/src/main/java/org/dinky/controller/AdminController.java @@ -25,8 +25,6 @@ import org.dinky.model.Tenant; import org.dinky.service.UserService; -import java.util.List; - import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -68,17 +66,13 @@ public Result outLogin() { /** 获取当前用户信息 */ @GetMapping("/current") - public Result current() { - try { - return Result.succeed(StpUtil.getSession().get("user"), "获取成功"); - } catch (Exception e) { - return Result.failed("获取失败"); - } + public Result current() { + return userService.queryCurrentUserInfo(); } - /** get tenant */ - @RequestMapping("/geTenants") - public Result> getTenants(@RequestParam("username") String username) { - return userService.getTenants(username); + /** choose tenant */ + @GetMapping("/chooseTenant") + public Result chooseTenant(@RequestParam("tenantId") Integer tenantId) { + return userService.chooseTenant(tenantId); } } diff --git a/dinky-admin/src/main/java/org/dinky/service/UserService.java b/dinky-admin/src/main/java/org/dinky/service/UserService.java index f8130f179b..157bcbe1f7 100644 --- a/dinky-admin/src/main/java/org/dinky/service/UserService.java +++ b/dinky-admin/src/main/java/org/dinky/service/UserService.java @@ -26,8 +26,6 @@ import org.dinky.model.Tenant; import org.dinky.model.User; -import java.util.List; - import com.fasterxml.jackson.databind.JsonNode; /** @@ -97,10 +95,17 @@ public interface UserService extends ISuperService { Result grantRole(JsonNode param); /** - * getTenants + * choose tenant * - * @param username username - * @return {@link Result}<{@link List}<{@link Tenant}>> + * @param tenantId + * @return + */ + Result chooseTenant(Integer tenantId); + + /** + * get current user base info + * + * @return */ - Result> getTenants(String username); + Result queryCurrentUserInfo(); } diff --git a/dinky-admin/src/main/java/org/dinky/service/impl/UserServiceImpl.java b/dinky-admin/src/main/java/org/dinky/service/impl/UserServiceImpl.java index c3fbb5f796..76202c566f 100644 --- a/dinky-admin/src/main/java/org/dinky/service/impl/UserServiceImpl.java +++ b/dinky-admin/src/main/java/org/dinky/service/impl/UserServiceImpl.java @@ -22,6 +22,7 @@ import org.dinky.assertion.Asserts; import org.dinky.common.result.Result; import org.dinky.context.TenantContextHolder; +import org.dinky.context.UserInfoContextHolder; import org.dinky.db.service.impl.SuperServiceImpl; import org.dinky.dto.LoginDTO; import org.dinky.dto.UserDTO; @@ -39,10 +40,8 @@ import org.dinky.utils.MessageResolverUtils; import java.util.ArrayList; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; -import java.util.Set; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -142,57 +141,48 @@ public Result loginUser(LoginDTO loginDTO) { if (!user.getEnabled()) { return Result.failed(MessageResolverUtils.getMessage("login.user.disabled")); } - - // 将前端入参 租户id 放入上下文 - TenantContextHolder.set(loginDTO.getTenantId()); - // get user tenants and roles - UserDTO userDTO = getUserAllBaseInfo(loginDTO, user); + UserDTO userInfo = refreshUserInfo(user); StpUtil.login(user.getId(), loginDTO.isAutoLogin()); - StpUtil.getSession().set("user", userDTO); - return Result.succeed(userDTO, MessageResolverUtils.getMessage("login.success")); + return Result.succeed(userInfo, MessageResolverUtils.getMessage("login.success")); } else { return Result.failed(MessageResolverUtils.getMessage("login.fail")); } } - private UserDTO getUserAllBaseInfo(LoginDTO loginDTO, User user) { - UserDTO userDTO = new UserDTO(); + private UserDTO refreshUserInfo(User user) { List roleList = new LinkedList<>(); List tenantList = new LinkedList<>(); List userRoles = userRoleService.getUserRoleByUserId(user.getId()); List userTenants = userTenantService.getUserTenantByUserId(user.getId()); - Tenant currentTenant = tenantService.getBaseMapper().selectById(loginDTO.getTenantId()); - - userRoles.forEach( - userRole -> { - Role role = roleService.getBaseMapper().selectById(userRole.getRoleId()); - if (Asserts.isNotNull(role)) { - roleList.add(role); - } - }); - - userTenants.forEach( - userTenant -> { - Tenant tenant = - tenantService - .getBaseMapper() - .selectOne( - new QueryWrapper() - .eq("id", userTenant.getTenantId())); - if (Asserts.isNotNull(tenant)) { - tenantList.add(tenant); - } - }); - - userDTO.setUser(user); - userDTO.setRoleList(roleList); - userDTO.setTenantList(tenantList); - userDTO.setCurrentTenant(currentTenant); - return userDTO; + userRoles.stream() + .forEach( + userRole -> { + Role role = + roleService.getBaseMapper().selectById(userRole.getRoleId()); + if (Asserts.isNotNull(role)) { + roleList.add(role); + } + }); + + userTenants.stream() + .forEach( + userTenant -> { + Tenant tenant = tenantService.getById(userTenant.getTenantId()); + if (Asserts.isNotNull(tenant)) { + tenantList.add(tenant); + } + }); + + UserDTO userInfo = new UserDTO(); + userInfo.setUser(user); + userInfo.setRoleList(roleList); + userInfo.setTenantList(tenantList); + UserInfoContextHolder.set(user.getId(), userInfo); + return userInfo; } @Override @@ -234,20 +224,32 @@ public Result grantRole(JsonNode param) { } @Override - public Result> getTenants(String username) { - User user = getUserByUsername(username); - if (Asserts.isNull(user)) { - return Result.failed("该账号不存在,获取租户失败"); + public Result chooseTenant(Integer tenantId) { + Tenant currentTenant = tenantService.getById(tenantId); + if (Asserts.isNull(currentTenant)) { + return Result.failed("Failed to obtain tenant information"); + } else { + UserDTO userInfo = UserInfoContextHolder.get(StpUtil.getLoginIdAsInt()); + userInfo.setCurrentTenant(currentTenant); + UserInfoContextHolder.refresh(StpUtil.getLoginIdAsInt(), userInfo); + TenantContextHolder.set(currentTenant.getId()); + return Result.succeed(currentTenant, "Tenant selected successfully"); } + } - List userTenants = userTenantService.getUserTenantByUserId(user.getId()); - if (userTenants.size() == 0) { - return Result.failed("用户未绑定租户,获取租户失败"); + @Override + public Result queryCurrentUserInfo() { + UserDTO userInfo = UserInfoContextHolder.get(StpUtil.getLoginIdAsInt()); + if (Asserts.isNotNull(userInfo) + && Asserts.isNotNull(userInfo.getUser()) + && Asserts.isNotNull(userInfo.getRoleList()) + && Asserts.isNotNull(userInfo.getTenantList()) + && Asserts.isNotNull(userInfo.getCurrentTenant())) { + StpUtil.getSession().set("user", userInfo); + return Result.succeed( + userInfo, MessageResolverUtils.getMessage("response.get.success")); + } else { + return Result.failed(userInfo, MessageResolverUtils.getMessage("response.get.failed")); } - - Set tenantIds = new HashSet<>(); - userTenants.forEach(userTenant -> tenantIds.add(userTenant.getTenantId())); - List tenants = tenantService.getTenantByIds(tenantIds); - return Result.succeed(tenants, MessageResolverUtils.getMessage("response.get.success")); } } diff --git a/dinky-web/README.md b/dinky-web/README.md index fc1e06e9db..f6cab8b0cf 100644 --- a/dinky-web/README.md +++ b/dinky-web/README.md @@ -2,13 +2,10 @@ This project uses [Ant Design Pro](https://pro.ant.design) for initialization. Here's a quick guide on how to use it. -[![CN doc](https://img.shields.io/badge/文档-中文版-blue.svg)](README_zh_CN.md) -[![EN doc](https://img.shields.io/badge/document-English-blue.svg)](README.md) - +[![CN doc](https://img.shields.io/badge/文档-中文版-blue.svg)](README_zh_CN.md) [![EN doc](https://img.shields.io/badge/document-English-blue.svg)](README.md) ## Environment preparation - | Environment | Version | Remarks | | ----------- | -------- | ------- | | node | 14.19.0+ | | @@ -41,3 +38,9 @@ You can also automatically fix some lint errors with a script: ```bash npm run lint:fix ``` + +### Code Format + +```bash +npm run prettier +``` diff --git a/dinky-web/README_zh_CN.md b/dinky-web/README_zh_CN.md index a2ee6b54fd..2b73cf5046 100644 --- a/dinky-web/README_zh_CN.md +++ b/dinky-web/README_zh_CN.md @@ -4,7 +4,6 @@ ## 环境准备 - | 环境 | 版本 | 备注 | | ---- | -------- | ---- | | node | 14.19.0+ | | @@ -33,6 +32,13 @@ npm run lint ``` 您还可以使用脚本自动修复一些 lint 错误: + ```bash npm run lint:fix ``` + +### 代码格式化 + +```bash +npm run prettier +``` diff --git a/dinky-web/config/config.ts b/dinky-web/config/config.ts index 5f817ff038..b5fe74415d 100644 --- a/dinky-web/config/config.ts +++ b/dinky-web/config/config.ts @@ -16,8 +16,8 @@ */ // https://umijs.org/config/ -import {defineConfig} from '@umijs/max'; -import {join} from 'path'; +import { defineConfig } from '@umijs/max'; +import { join } from 'path'; import defaultSettings from './defaultSettings'; import proxy from './proxy'; import routes from './routes'; @@ -31,7 +31,9 @@ export default defineConfig({ * @doc https://umijs.org/docs/api/config#hash */ hash: true, - + history: { + type: 'hash', + }, /** * @name 兼容性设置 * @description 设置 ie11 不一定完美兼容,需要检查自己使用的所有依赖 diff --git a/dinky-web/config/defaultSettings.ts b/dinky-web/config/defaultSettings.ts index 857126563d..d1baed2e4b 100644 --- a/dinky-web/config/defaultSettings.ts +++ b/dinky-web/config/defaultSettings.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import {ProLayoutProps} from '@ant-design/pro-components'; +import { ProLayoutProps } from '@ant-design/pro-components'; /** * @name @@ -38,7 +38,7 @@ const Settings: ProLayoutProps & { iconfontUrl: '', splitMenus: true, menu: { - locale: true + locale: true, }, token: { // bgLayout: '', layout 的背景颜色 diff --git a/dinky-web/config/proxy.ts b/dinky-web/config/proxy.ts index bee081d32d..438273b0c3 100644 --- a/dinky-web/config/proxy.ts +++ b/dinky-web/config/proxy.ts @@ -32,11 +32,12 @@ export default { // localhost:8000/api/** -> https://preview.pro.ant.design/api/** '/api/': { // 要代理的地址 - // target: 'http://127.0.0.1:8888/', + target: 'http://127.0.0.1:8888', // target: 'https://preview.pro.ant.design', // 配置了这个可以从 http 代理到 https // 依赖 origin 的功能可能需要这个,比如 cookie changeOrigin: true, + pathRewrite: { '^': '' }, }, }, diff --git a/dinky-web/config/routes.ts b/dinky-web/config/routes.ts index db1de0d8ea..c7ec97ef62 100644 --- a/dinky-web/config/routes.ts +++ b/dinky-web/config/routes.ts @@ -136,17 +136,19 @@ export default [ // component: './RegistrationCenter/AlertManage/AlertGroup', }, ], - }, { + }, + { path: '/registration/document', name: 'document', icon: 'container', // component: './RegistrationCenter/Document', - },{ + }, + { path: '/registration/fragment', name: 'fragment', - icon: "cloud", + icon: 'cloud', // component: './RegistrationCenter/FragmentVariable', - } + }, ], }, { diff --git a/dinky-web/jest.config.ts b/dinky-web/jest.config.ts index 5e527a687e..79192b98fd 100644 --- a/dinky-web/jest.config.ts +++ b/dinky-web/jest.config.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import {configUmiAlias, createConfig} from '@umijs/max/test'; +import { configUmiAlias, createConfig } from '@umijs/max/test'; export default async () => { const config = await configUmiAlias({ diff --git a/dinky-web/mock/listTableList.ts b/dinky-web/mock/listTableList.ts index a586d81f9a..c47f321623 100644 --- a/dinky-web/mock/listTableList.ts +++ b/dinky-web/mock/listTableList.ts @@ -15,9 +15,9 @@ * limitations under the License. */ -import {Request, Response} from 'express'; +import { Request, Response } from 'express'; import moment from 'moment'; -import {parse} from 'url'; +import { parse } from 'url'; // mock tableListDataSource const genList = (current: number, pageSize: number) => { diff --git a/dinky-web/mock/user.ts b/dinky-web/mock/user.ts index 377b799cf3..6d566798dd 100644 --- a/dinky-web/mock/user.ts +++ b/dinky-web/mock/user.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import {Request, Response} from 'express'; +import { Request, Response } from 'express'; const waitTime = (time: number = 100) => { return new Promise((resolve) => { diff --git a/dinky-web/package.json b/dinky-web/package.json index c9bde79857..03a8ec5414 100644 --- a/dinky-web/package.json +++ b/dinky-web/package.json @@ -60,7 +60,8 @@ "react": "^18.0.0", "react-dev-inspector": "^1.8.1", "react-dom": "^18.0.0", - "react-helmet-async": "^1.3.0" + "react-helmet-async": "^1.3.0", + "screenfull": "^6.0.2" }, "devDependencies": { "@ant-design/pro-cli": "^2.1.0", diff --git a/dinky-web/src/access.ts b/dinky-web/src/access.ts index 160c1694ce..dab36983f6 100644 --- a/dinky-web/src/access.ts +++ b/dinky-web/src/access.ts @@ -21,6 +21,6 @@ export default function access(initialState: { currentUser?: API.CurrentUser } | undefined) { const { currentUser } = initialState ?? {}; return { - canAdmin: currentUser && currentUser.access === 'admin', + canAdmin: currentUser && currentUser.user.isAdmin, }; } diff --git a/dinky-web/src/app.tsx b/dinky-web/src/app.tsx index 3e4a2f8d4c..62cc4f5246 100644 --- a/dinky-web/src/app.tsx +++ b/dinky-web/src/app.tsx @@ -25,7 +25,6 @@ import { history, Link } from '@umijs/max'; import defaultSettings from '../config/defaultSettings'; import { errorConfig } from './requestErrorConfig'; import { currentUser as queryCurrentUser } from './services/api'; -import React from 'react'; const isDev = process.env.NODE_ENV === 'development'; const loginPath = '/user/login'; @@ -42,19 +41,22 @@ export async function getInitialState(): Promise<{ const fetchUserInfo = async () => { try { const result = await queryCurrentUser(); + const user = result.datas.user; const currentUser: API.CurrentUser = { - id: result.datas.user.id, - username: result.datas.user.username, - password: result.datas.user.password, - nickname: result.datas.user.nickname, - worknum: result.datas.user.worknum, - avatar: result.datas.user.avatar ? result.datas.user.avatar : '/icons/user_avatar.png', - mobile: result.datas.user.mobile, - enabled: result.datas.user.enabled, - isDelete: result.datas.user.isDelete, - createTime: result.datas.user.createTime, - updateTime: result.datas.user.updateTime, - isAdmin: result.datas.user.isAdmin, + user: { + id: user.id, + username: user.username, + password: user.password, + nickname: user.nickname, + worknum: user.worknum, + avatar: user.avatar ? user.avatar : '/icons/user_avatar.png', + mobile: user.mobile, + enabled: user.enabled, + isDelete: user.isDelete, + createTime: user.createTime, + updateTime: user.updateTime, + isAdmin: user.isAdmin, + }, roleList: result.datas.roleList, tenantList: result.datas.tenantList, currentTenant: result.datas.currentTenant, @@ -66,7 +68,7 @@ export async function getInitialState(): Promise<{ return undefined; }; // 如果不是登录页面,执行 - const {location} = history; + const { location } = history; if (location.pathname !== loginPath) { const currentUser = await fetchUserInfo(); return { @@ -82,12 +84,12 @@ export async function getInitialState(): Promise<{ } // ProLayout 支持的api https://procomponents.ant.design/components/layout -export const layout: RunTimeLayoutConfig = ({initialState, setInitialState}) => { +export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => { return { rightContentRender: () => , footerRender: () =>