parent
0570ddd249
commit
0e84f0934e
|
@ -3,16 +3,12 @@ package impl;
|
|||
import cn.bunny.services.domain.common.model.dto.excel.I18nExcel;
|
||||
import cn.bunny.services.domain.system.i18n.entity.I18n;
|
||||
import cn.bunny.services.mapper.configuration.I18nMapper;
|
||||
import cn.bunny.services.utils.i8n.I18nUtil;
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -21,34 +17,6 @@ import java.util.zip.ZipOutputStream;
|
|||
|
||||
public class I18nServiceImplTest extends ServiceImpl<I18nMapper, I18n> {
|
||||
|
||||
@Test
|
||||
void downloadI18n() {
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream);
|
||||
|
||||
// 查找默认语言内容
|
||||
List<I18n> i18nList = list();
|
||||
HashMap<String, Object> hashMap = I18nUtil.getMap(i18nList);
|
||||
|
||||
hashMap.forEach((k, v) -> {
|
||||
try {
|
||||
ZipEntry zipEntry = new ZipEntry(k + ".json");
|
||||
zipOutputStream.putNextEntry(zipEntry);
|
||||
|
||||
zipOutputStream.write(JSON.toJSONString(v).getBytes(StandardCharsets.UTF_8));
|
||||
zipOutputStream.closeEntry();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
zipOutputStream.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void downloadI18nByExcel() {
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
|
|
|
@ -4,14 +4,9 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
public class SecurityConfigConstant {
|
||||
public static String[] REQUEST_MATCHERS_PERMIT_ALL = {
|
||||
"/", "/**.html", "/error", "/media.ico", "/favicon.ico",
|
||||
"/webjars/**", "/v3/api-docs/**", "/swagger-ui/**",
|
||||
"/*/*/noAuth/**", "/*/noAuth/**", "/noAuth/**",
|
||||
"/*/i18n/getI18n"
|
||||
};
|
||||
|
||||
public static List<String> PERMIT_ALL_LIST = new ArrayList<>() {{
|
||||
/* 可以放行的权限 */
|
||||
public static List<String> PERMIT_ACCESS_LIST = new ArrayList<>() {{
|
||||
add("admin");
|
||||
}};
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import cn.bunny.services.domain.system.system.entity.Role;
|
|||
import cn.bunny.services.mapper.system.PermissionMapper;
|
||||
import cn.bunny.services.mapper.system.RoleMapper;
|
||||
import cn.bunny.services.security.config.WebSecurityConfig;
|
||||
import cn.bunny.services.utils.system.RoleUtil;
|
||||
import cn.bunny.services.service.system.helper.role.RoleHelper;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
@ -44,7 +44,7 @@ public class PermissionCheckService {
|
|||
List<String> roleCodeList = roleList.stream().map(Role::getRoleCode).toList();
|
||||
|
||||
// 判断是否是管理员用户
|
||||
boolean checkedAdmin = RoleUtil.checkAdmin(roleCodeList);
|
||||
boolean checkedAdmin = RoleHelper.checkAdmin(roleCodeList);
|
||||
if (checkedAdmin) return true;
|
||||
|
||||
// 判断请求地址是否是登录之后才需要访问的,已经登录了不需要验证的
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package cn.bunny.services.utils.email;
|
||||
package cn.bunny.services.service.configuration.helper.email;
|
||||
|
||||
import cn.bunny.services.domain.common.model.dto.email.EmailSend;
|
||||
import cn.bunny.services.domain.common.model.dto.email.EmailSendInit;
|
|
@ -1,4 +1,4 @@
|
|||
package cn.bunny.services.utils.email;
|
||||
package cn.bunny.services.service.configuration.helper.email;
|
||||
|
||||
import cn.bunny.services.domain.system.email.entity.EmailTemplate;
|
||||
import cn.bunny.services.mapper.configuration.EmailTemplateMapper;
|
|
@ -1,7 +1,8 @@
|
|||
package cn.bunny.services.utils.i8n;
|
||||
package cn.bunny.services.service.configuration.helper.i18n;
|
||||
|
||||
import cn.bunny.services.domain.common.model.dto.excel.I18nExcel;
|
||||
import cn.bunny.services.domain.system.i18n.entity.I18n;
|
||||
import cn.bunny.services.service.configuration.impl.I18nServiceImpl;
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
@ -16,46 +17,20 @@ import java.util.stream.Collectors;
|
|||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
public class I18nUtil {
|
||||
@NotNull
|
||||
public static HashMap<String, Object> getMap(@NotNull List<I18n> i18nList) {
|
||||
// 整理集合
|
||||
Map<String, Map<String, String>> map = i18nList.stream()
|
||||
.collect(Collectors.groupingBy(
|
||||
I18n::getTypeName,
|
||||
Collectors.toMap(I18n::getKeyName, I18n::getTranslation)));
|
||||
|
||||
// 返回集合
|
||||
return new HashMap<>(map);
|
||||
}
|
||||
|
||||
public class I18nHelper {
|
||||
/**
|
||||
* 使用zip写入json
|
||||
* 将国际化资源列表写入Excel并打包到ZIP输出流
|
||||
*
|
||||
* @param i18nList i18nList
|
||||
* @param zipOutputStream zipOutputStream
|
||||
*/
|
||||
public static void writeJson(List<I18n> i18nList, ZipOutputStream zipOutputStream) {
|
||||
HashMap<String, Object> hashMap = getMap(i18nList);
|
||||
|
||||
hashMap.forEach((k, v) -> {
|
||||
try {
|
||||
ZipEntry zipEntry = new ZipEntry(k + ".json");
|
||||
zipOutputStream.putNextEntry(zipEntry);
|
||||
|
||||
zipOutputStream.write(JSON.toJSONString(v).getBytes(StandardCharsets.UTF_8));
|
||||
zipOutputStream.closeEntry();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用zip写入excel
|
||||
* <p>处理流程:</p>
|
||||
* <ol>
|
||||
* <li>按资源类型(typeName)分组 --- 多语言的类型,英文?中文?</li>
|
||||
* <li>每组数据生成单独多语言 key的Excel工作表</li>
|
||||
* <li>将Excel文件写入ZIP输出流</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param i18nList i18nList
|
||||
* @param zipOutputStream zipOutputStream
|
||||
* @param i18nList 国际化资源列表,包含key-value对和类型信息
|
||||
* @param zipOutputStream ZIP输出流,用于写入打包后的Excel文件
|
||||
* @throws RuntimeException 当IO操作失败时抛出
|
||||
*/
|
||||
public static void writeExcel(List<I18n> i18nList, ZipOutputStream zipOutputStream) {
|
||||
Map<String, List<I18nExcel>> hashMap = i18nList.stream()
|
||||
|
@ -86,4 +61,62 @@ public class I18nUtil {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 将国际化资源列表写入JSON并打包到ZIP输出流
|
||||
*
|
||||
* <p>处理流程:</p>
|
||||
* <ol>
|
||||
* <li>按资源类型(typeName)分组 --- 多语言的类型,英文?中文?</li>
|
||||
* <li>每组数据生成单独多语言 key的Excel工作表</li>
|
||||
* <li>将JSON文件写入ZIP输出流</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param i18nList 国际化资源列表
|
||||
* @param zipOutputStream ZIP输出流
|
||||
* @throws RuntimeException 当IO操作失败时抛出
|
||||
*/
|
||||
public static void writeJson(List<I18n> i18nList, ZipOutputStream zipOutputStream) {
|
||||
HashMap<String, Object> hashMap = getMapByI18nList(i18nList);
|
||||
|
||||
hashMap.forEach((k, v) -> {
|
||||
try {
|
||||
ZipEntry zipEntry = new ZipEntry(k + ".json");
|
||||
zipOutputStream.putNextEntry(zipEntry);
|
||||
|
||||
zipOutputStream.write(JSON.toJSONString(v).getBytes(StandardCharsets.UTF_8));
|
||||
zipOutputStream.closeEntry();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 将国际化资源列表转换为结构化Map
|
||||
*
|
||||
* <p>转换规则:</p>
|
||||
* <ul>
|
||||
* <li>外层Key: 资源类型(typeName)</li>
|
||||
* <li>内层Key: 资源键名(keyName)</li>
|
||||
* <li>值: 翻译文本(translation)</li>
|
||||
* </ul>
|
||||
* <p>详细结构和结果示例看前端传递的 {@link I18nServiceImpl#getI18nMap} 控制器</p>
|
||||
* <p>/api/i18n/public</p>
|
||||
*
|
||||
* @param i18nList 国际化资源列表
|
||||
* @return 结构化Map {typeName: {keyName: translation}}
|
||||
* @throws IllegalArgumentException 当参数为null时抛出
|
||||
*/
|
||||
@NotNull
|
||||
public static HashMap<String, Object> getMapByI18nList(@NotNull List<I18n> i18nList) {
|
||||
// 整理集合
|
||||
Map<String, Map<String, String>> map = i18nList.stream()
|
||||
.collect(Collectors.groupingBy(
|
||||
I18n::getTypeName,
|
||||
Collectors.toMap(I18n::getKeyName, I18n::getTranslation)));
|
||||
|
||||
// 返回集合
|
||||
return new HashMap<>(map);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package cn.bunny.services.service.configuration.impl;
|
||||
|
||||
import cn.bunny.services.domain.common.constant.FileType;
|
||||
import cn.bunny.services.domain.common.model.entity.BaseEntity;
|
||||
import cn.bunny.services.domain.common.model.dto.excel.I18nExcel;
|
||||
import cn.bunny.services.domain.common.model.entity.BaseEntity;
|
||||
import cn.bunny.services.domain.common.model.vo.result.PageResult;
|
||||
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
|
||||
import cn.bunny.services.domain.system.i18n.dto.I18nAddDto;
|
||||
|
@ -17,8 +17,8 @@ import cn.bunny.services.exception.AuthCustomerException;
|
|||
import cn.bunny.services.mapper.configuration.I18nMapper;
|
||||
import cn.bunny.services.mapper.configuration.I18nTypeMapper;
|
||||
import cn.bunny.services.service.configuration.I18nService;
|
||||
import cn.bunny.services.service.configuration.helper.i18n.I18nHelper;
|
||||
import cn.bunny.services.utils.FileUtil;
|
||||
import cn.bunny.services.utils.i8n.I18nUtil;
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.TypeReference;
|
||||
|
@ -76,7 +76,7 @@ public class I18nServiceImpl extends ServiceImpl<I18nMapper, I18n> implements I1
|
|||
I18nType i18nType = i18nTypeMapper.selectOne(Wrappers.<I18nType>lambdaQuery().eq(I18nType::getIsDefault, true));
|
||||
List<I18n> i18nList = list();
|
||||
|
||||
HashMap<String, Object> hashMap = I18nUtil.getMap(i18nList);
|
||||
HashMap<String, Object> hashMap = I18nHelper.getMapByI18nList(i18nList);
|
||||
hashMap.put("local", Objects.requireNonNull(i18nType.getTypeName(), "zh"));
|
||||
|
||||
return hashMap;
|
||||
|
@ -170,15 +170,11 @@ public class I18nServiceImpl extends ServiceImpl<I18nMapper, I18n> implements I1
|
|||
|
||||
// 类型是Excel写入Excel
|
||||
if (type.equals(FileType.EXCEL)) {
|
||||
I18nUtil.writeExcel(i18nList, zipOutputStream);
|
||||
I18nHelper.writeExcel(i18nList, zipOutputStream);
|
||||
}
|
||||
// 其他格式写入JSON
|
||||
else {
|
||||
I18nUtil.writeJson(i18nList, zipOutputStream);
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
I18nHelper.writeJson(i18nList, zipOutputStream);
|
||||
}
|
||||
|
||||
// 设置响应头
|
||||
|
@ -186,6 +182,9 @@ public class I18nServiceImpl extends ServiceImpl<I18nMapper, I18n> implements I1
|
|||
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
|
||||
return new ResponseEntity<>(byteArrayInputStream.readAllBytes(), headers, HttpStatus.OK);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package cn.bunny.services.utils.system;
|
||||
package cn.bunny.services.service.system.helper;
|
||||
|
||||
import cn.bunny.services.domain.common.model.dto.excel.PermissionExcel;
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
|
@ -11,12 +11,20 @@ import java.util.List;
|
|||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
public class PermissionUtil {
|
||||
/**
|
||||
* 将属性结构扁平化
|
||||
/**
|
||||
* 权限数据处理工具类
|
||||
*
|
||||
* @param list 属性结构
|
||||
* @return 扁平化数组
|
||||
* <p>提供权限数据的树形结构处理、扁平化处理以及导出功能</p>
|
||||
*/
|
||||
public class PermissionHelper {
|
||||
|
||||
/**
|
||||
* 将树形结构权限数据扁平化为列表
|
||||
*
|
||||
* <p>使用递归处理树形结构</p>
|
||||
*
|
||||
* @param list 树形结构的权限列表,每个节点可能包含children子节点
|
||||
* @return 扁平化后的权限列表(
|
||||
*/
|
||||
public static List<PermissionExcel> flattenTree(List<PermissionExcel> list) {
|
||||
List<PermissionExcel> result = new ArrayList<>();
|
||||
|
@ -32,10 +40,12 @@ public class PermissionUtil {
|
|||
}
|
||||
|
||||
/**
|
||||
* 设置子集
|
||||
* 递归设置子节点(内部方法)
|
||||
*
|
||||
* @param parent 父级节点
|
||||
* @param list 要构建的列表
|
||||
* <p>为父节点查找并设置所有子节点,递归处理子节点的子节点</p>
|
||||
*
|
||||
* @param parent 当前父节点
|
||||
* @param list 完整的权限数据列表
|
||||
*/
|
||||
private static void setChildren(PermissionExcel parent, List<PermissionExcel> list) {
|
||||
List<PermissionExcel> children = list.stream()
|
||||
|
@ -51,12 +61,13 @@ public class PermissionUtil {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构建属性结构
|
||||
* 构建权限树形结构
|
||||
*
|
||||
* @param list 要构建的列表
|
||||
* @return 构建完成的列表
|
||||
* <p>从扁平列表中构建树形结构,根节点的判断条件为parentId为null或0</p>
|
||||
*
|
||||
* @param list 扁平化的权限数据列表
|
||||
* @return 构建完成的树形结构列表(只包含根节点)
|
||||
*/
|
||||
public static List<PermissionExcel> buildTree(List<PermissionExcel> list) {
|
||||
List<PermissionExcel> permissionExcels = list.stream()
|
||||
|
@ -70,11 +81,11 @@ public class PermissionUtil {
|
|||
}
|
||||
|
||||
/**
|
||||
* 写入JSON
|
||||
* 将权限数据写入JSON格式到ZIP压缩包
|
||||
*
|
||||
* @param list 写入的列表
|
||||
* @param zipOutputStream zip输出流
|
||||
* @param zipName zip文件名
|
||||
* @param list 要导出的权限数据列表
|
||||
* @param zipOutputStream ZIP输出流
|
||||
* @param zipName 在ZIP包中的文件名
|
||||
*/
|
||||
public static void writeJson(List<PermissionExcel> list, ZipOutputStream zipOutputStream, String zipName) {
|
||||
try {
|
||||
|
@ -88,11 +99,11 @@ public class PermissionUtil {
|
|||
}
|
||||
|
||||
/**
|
||||
* 写入JSON
|
||||
* 将权限数据写入Excel格式到ZIP压缩包
|
||||
*
|
||||
* @param list 写入的列表
|
||||
* @param zipOutputStream zip输出流
|
||||
* @param zipName zip文件名
|
||||
* @param list 要导出的权限数据列表
|
||||
* @param zipOutputStream ZIP输出流
|
||||
* @param zipName 在ZIP包中的文件名(需包含.xlsx后缀)
|
||||
*/
|
||||
public static void writExcel(List<PermissionExcel> list, ZipOutputStream zipOutputStream, String zipName) {
|
||||
try {
|
|
@ -1,13 +1,14 @@
|
|||
package cn.bunny.services.utils.system;
|
||||
package cn.bunny.services.service.system.helper;
|
||||
|
||||
import cn.bunny.services.context.BaseContext;
|
||||
import cn.bunny.services.domain.system.system.entity.RouterRole;
|
||||
import cn.bunny.services.domain.system.system.entity.router.Router;
|
||||
import cn.bunny.services.domain.system.system.entity.router.RouterMeta;
|
||||
import cn.bunny.services.domain.system.system.views.ViewRolePermission;
|
||||
import cn.bunny.services.domain.system.system.views.ViewRouterRole;
|
||||
import cn.bunny.services.domain.system.system.vo.router.WebUserRouterVo;
|
||||
import cn.bunny.services.context.BaseContext;
|
||||
import cn.bunny.services.service.system.RouterRoleService;
|
||||
import cn.bunny.services.service.system.helper.role.RoleHelper;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -20,7 +21,7 @@ import java.util.*;
|
|||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RouterUtil {
|
||||
public class RouterHelper {
|
||||
@Resource
|
||||
private RouterRoleService routerRoleService;
|
||||
|
||||
|
@ -44,10 +45,12 @@ public class RouterUtil {
|
|||
}
|
||||
|
||||
/**
|
||||
* 查询新的路由权限和角色
|
||||
* 保存路由角色关联关系
|
||||
*
|
||||
* @param meta RouterMeta
|
||||
* @param id 路由id
|
||||
* <p>将路由的权限角色配置保存到数据库</p>
|
||||
*
|
||||
* @param meta 路由元数据,包含角色配置信息
|
||||
* @param id 路由ID
|
||||
*/
|
||||
public void insertRouterRoleAndPermission(RouterMeta meta, Long id) {
|
||||
List<String> roles = meta.getRoles();
|
||||
|
@ -63,18 +66,31 @@ public class RouterUtil {
|
|||
}
|
||||
|
||||
/**
|
||||
* 整理web用户所能看到的路由列表
|
||||
* 构建前端可访问的路由列表
|
||||
*
|
||||
* @param routerList 所有的路由列表
|
||||
* @param routerRoleList 路由和角色列表
|
||||
* @param rolePermissionList 角色和权限列表
|
||||
* @return web用户所能看到的路由列表
|
||||
* <p>处理流程:</p>
|
||||
* <ol>
|
||||
* <li>检查当前用户是否为管理员(拥有全部权限)</li>
|
||||
* <li>转换路由基础信息</li>
|
||||
* <li>处理路由元数据(meta)</li>
|
||||
* <li>设置路由关联的角色信息</li>
|
||||
* <li>设置路由关联的权限信息</li>
|
||||
* <li>按rank排序返回</li>
|
||||
* </ol>
|
||||
* <a href="https://pure-admin.cn/pages/routerMenu/#%E8%B7%AF%E7%94%B1%E5%92%8C%E8%8F%9C%E5%8D%95%E9%85%8D%E7%BD%AE">
|
||||
* 路由结构参考这个文档
|
||||
* </a>
|
||||
*
|
||||
* @param routerList 所有路由列表
|
||||
* @param routerRoleList 路由-角色关联Map(key: 路由ID, value: 角色列表)
|
||||
* @param rolePermissionList 角色-权限关联Map(key: 角色ID, value: 权限列表)
|
||||
* @return 前端可访问的路由列表(包含完整的权限信息)
|
||||
*/
|
||||
@NotNull
|
||||
public List<WebUserRouterVo> getWebUserRouterVos(List<Router> routerList, Map<Long, List<ViewRouterRole>> routerRoleList, Map<Long, List<ViewRolePermission>> rolePermissionList) {
|
||||
// 检查当前是否是 admin 用户
|
||||
List<String> roles = BaseContext.getLoginVo().getRoles();
|
||||
List<String> allAuths = !RoleUtil.checkAdmin(roles) ? new ArrayList<>() : List.of("*:*:*", "*:*", "*", "admin");
|
||||
List<String> allAuths = !RoleHelper.checkAdmin(roles) ? new ArrayList<>() : List.of("*:*:*", "*:*", "*", "admin");
|
||||
|
||||
// 查询路由所有数据,整理前端需要的路和、角色、权限
|
||||
return routerList.stream().map(view -> {
|
|
@ -13,9 +13,9 @@ import cn.bunny.services.mapper.system.PermissionMapper;
|
|||
import cn.bunny.services.mapper.system.RoleMapper;
|
||||
import cn.bunny.services.mapper.system.UserMapper;
|
||||
import cn.bunny.services.minio.MinioHelper;
|
||||
import cn.bunny.services.service.system.helper.role.RoleHelper;
|
||||
import cn.bunny.services.utils.IpUtil;
|
||||
import cn.bunny.services.utils.JwtTokenUtil;
|
||||
import cn.bunny.services.utils.system.RoleUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -94,7 +94,7 @@ public class UserLoginHelper {
|
|||
|
||||
// 判断是否是 admin 如果是admin 赋予所有权限
|
||||
List<String> permissions = new ArrayList<>();
|
||||
boolean isAdmin = RoleUtil.checkAdmin(roles, permissions, user);
|
||||
boolean isAdmin = RoleHelper.checkAdmin(roles, permissions, user);
|
||||
if (!isAdmin) {
|
||||
permissions = permissionMapper.selectListByUserId(userId).stream()
|
||||
.map(Permission::getPowerCode)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package cn.bunny.services.utils.system;
|
||||
package cn.bunny.services.service.system.helper.role;
|
||||
|
||||
import cn.bunny.services.context.BaseContext;
|
||||
import cn.bunny.services.domain.common.constant.RedisUserConstant;
|
||||
|
@ -6,7 +6,6 @@ import cn.bunny.services.domain.common.constant.SecurityConfigConstant;
|
|||
import cn.bunny.services.domain.system.system.entity.AdminUser;
|
||||
import cn.bunny.services.mapper.system.UserMapper;
|
||||
import cn.bunny.services.service.system.helper.UserLoginHelper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
@ -14,7 +13,7 @@ import org.springframework.stereotype.Component;
|
|||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class RoleUtil {
|
||||
public class RoleHelper {
|
||||
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
@ -73,36 +72,53 @@ public class RoleUtil {
|
|||
*/
|
||||
public static boolean checkAdmin(List<String> roleList) {
|
||||
// 可以放行的权限
|
||||
List<String> permitAllList = SecurityConfigConstant.PERMIT_ALL_LIST;
|
||||
List<String> permissionList = SecurityConfigConstant.PERMIT_ACCESS_LIST;
|
||||
|
||||
// 判断是否是超级管理员
|
||||
if (BaseContext.getUserId().equals(1L)) return true;
|
||||
|
||||
// 判断是否是 admin
|
||||
return roleList.stream().anyMatch(permitAllList::contains);
|
||||
return roleList.stream().anyMatch(permissionList::contains);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新Redis中用户信息
|
||||
* 批量更新Redis中用户权限信息
|
||||
*
|
||||
* @param userIds 用户Id列表
|
||||
* <p><b>使用场景</b>:当用户角色或权限变更时,同步更新Redis中的用户权限数据</p>
|
||||
*
|
||||
* <p><b>实现策略</b>:</p>
|
||||
* <ol>
|
||||
* <li><b>主动更新(当前实现)</b>:重新构建用户权限信息并更新Redis缓存</li>
|
||||
* <li><b>强制下线</b>:删除用户登录态,强制重新认证获取最新权限</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p><b>技术实现</b>:</p>
|
||||
* <ul>
|
||||
* <li>采用Spring事件驱动机制触发更新</li>
|
||||
* <li>使用并行流(parallelStream)提高批量处理效率</li>
|
||||
* <li>仅更新Redis中存在登录态的用户</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param userIds 需要更新的用户ID集合
|
||||
* (仅处理集合中存在的有效用户)
|
||||
* @see RedisUserConstant Redis键前缀常量
|
||||
* @see UserLoginHelper#buildLoginUserVo 用户登录信息构建方法
|
||||
*/
|
||||
public void updateUserRedisInfo(List<Long> userIds) {
|
||||
if (userIds.isEmpty()) return;
|
||||
|
||||
// 根据Id查找所有用户
|
||||
List<AdminUser> adminUsers = userMapper.selectList(Wrappers.<AdminUser>lambdaQuery().in(AdminUser::getId, userIds));
|
||||
// 批量查询用户
|
||||
List<AdminUser> adminUsers = userMapper.selectBatchIds(userIds);
|
||||
|
||||
// 用户为空时不更新Redis的key
|
||||
if (adminUsers.isEmpty()) return;
|
||||
// 并行处理用户更新
|
||||
adminUsers.parallelStream()
|
||||
.filter(user -> redisTemplate.hasKey(RedisUserConstant.getAdminLoginInfoPrefix(user.getUsername())))
|
||||
.forEach(user -> {
|
||||
// 策略1: 更新用户权限信息
|
||||
userloginHelper.buildLoginUserVo(user, RedisUserConstant.REDIS_EXPIRATION_TIME);
|
||||
|
||||
// 更新Redis中用户信息
|
||||
adminUsers.stream()
|
||||
.filter(user -> {
|
||||
String adminLoginInfoPrefix = RedisUserConstant.getAdminLoginInfoPrefix(user.getUsername());
|
||||
Object object = redisTemplate.opsForValue().get(adminLoginInfoPrefix);
|
||||
return object != null;
|
||||
})
|
||||
.forEach(user -> userloginHelper.buildLoginUserVo(user, RedisUserConstant.REDIS_EXPIRATION_TIME));
|
||||
// 或者策略2: 强制用户下线
|
||||
// redisTemplate.delete(RedisUserConstant.getAdminLoginInfoPrefix(user.getUsername()));
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package cn.bunny.services.service.system.helper.role;
|
||||
|
||||
import cn.bunny.services.domain.system.system.entity.UserRole;
|
||||
import cn.bunny.services.mapper.system.UserRoleMapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 角色更新事件处理器
|
||||
*
|
||||
* <p><b>职责说明</b>:监听并处理角色变更事件,同步更新关联用户的权限信息</p>
|
||||
*
|
||||
* <p><b>处理流程</b>:</p>
|
||||
* <ol>
|
||||
* <li>监听{@code RoleUpdatedEvent}事件</li>
|
||||
* <li>查询关联该角色的所有用户ID</li>
|
||||
* <li>通过{@code RoleHelper}批量更新用户Redis缓存</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p><b>注意事项</b>:</p>
|
||||
* <ul>
|
||||
* <li>使用异步处理(@{@link Async})避免阻塞主线程</li>
|
||||
* <li>仅处理直接关联的用户,不包含通过用户组等间接关联的情况</li>
|
||||
* <li>更新操作采用批量处理提高效率</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Component
|
||||
public class RoleUpdateHandler {
|
||||
@Resource
|
||||
private UserRoleMapper userRoleMapper;
|
||||
|
||||
@Resource
|
||||
private RoleHelper roleHelper;
|
||||
|
||||
/**
|
||||
* 处理角色更新事件
|
||||
*
|
||||
* @param event 角色更新事件,包含变更的角色ID
|
||||
* @see RoleUpdatedEvent 角色更新事件定义
|
||||
* @see RoleHelper#updateUserRedisInfo 用户缓存更新方法
|
||||
*/
|
||||
@Async
|
||||
@EventListener
|
||||
public void handleRoleUpdatedEvent(RoleUpdatedEvent event) {
|
||||
// 查询当前用户角色中角色id
|
||||
LambdaQueryWrapper<UserRole> lambdaQueryWrapper = Wrappers.<UserRole>lambdaQuery().eq(UserRole::getRoleId, event.getRoleId());
|
||||
|
||||
// 批量查询关联用户ID
|
||||
List<UserRole> userRoles = userRoleMapper.selectList(lambdaQueryWrapper);
|
||||
List<Long> userIds = userRoles.stream().map(UserRole::getUserId).toList();
|
||||
roleHelper.updateUserRedisInfo(userIds);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package cn.bunny.services.service.system.helper.role;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
// 角色更新事件
|
||||
@Getter
|
||||
@Setter
|
||||
public class RoleUpdatedEvent extends ApplicationEvent {
|
||||
private final Long roleId;
|
||||
|
||||
public RoleUpdatedEvent(Object source, Long roleId) {
|
||||
super(source);
|
||||
this.roleId = roleId;
|
||||
}
|
||||
}
|
|
@ -15,8 +15,8 @@ import cn.bunny.services.exception.AuthCustomerException;
|
|||
import cn.bunny.services.mapper.system.PermissionMapper;
|
||||
import cn.bunny.services.mapper.system.RolePermissionMapper;
|
||||
import cn.bunny.services.service.system.PermissionService;
|
||||
import cn.bunny.services.service.system.helper.PermissionHelper;
|
||||
import cn.bunny.services.utils.FileUtil;
|
||||
import cn.bunny.services.utils.system.PermissionUtil;
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.TypeReference;
|
||||
|
@ -187,7 +187,7 @@ public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permiss
|
|||
}).toList();
|
||||
|
||||
// 构建树型结构
|
||||
List<PermissionExcel> buildTree = PermissionUtil.buildTree(permissionExcelList);
|
||||
List<PermissionExcel> buildTree = PermissionHelper.buildTree(permissionExcelList);
|
||||
|
||||
// 创建btye输出流
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
|
@ -197,9 +197,9 @@ public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permiss
|
|||
|
||||
// 判断导出类型是什么
|
||||
if (type.equals(FileType.EXCEL)) {
|
||||
PermissionUtil.writExcel(permissionExcelList, zipOutputStream, filename + ".xlsx");
|
||||
PermissionHelper.writExcel(permissionExcelList, zipOutputStream, filename + ".xlsx");
|
||||
} else {
|
||||
PermissionUtil.writeJson(buildTree, zipOutputStream, filename + ".json");
|
||||
PermissionHelper.writeJson(buildTree, zipOutputStream, filename + ".json");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
|
@ -235,7 +235,7 @@ public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permiss
|
|||
List<PermissionExcel> list = JSON.parseObject(json, new TypeReference<>() {
|
||||
});
|
||||
// 格式化数据,保存到数据库
|
||||
List<PermissionExcel> flattenedTree = PermissionUtil.flattenTree(list);
|
||||
List<PermissionExcel> flattenedTree = PermissionHelper.flattenTree(list);
|
||||
List<Permission> permissionList = flattenedTree.stream().map(permissionExcel -> {
|
||||
Permission permission = new Permission();
|
||||
BeanUtils.copyProperties(permissionExcel, permission);
|
||||
|
|
|
@ -8,7 +8,7 @@ import cn.bunny.services.mapper.system.RolePermissionMapper;
|
|||
import cn.bunny.services.mapper.system.UserMapper;
|
||||
import cn.bunny.services.mapper.system.UserRoleMapper;
|
||||
import cn.bunny.services.service.system.RolePermissionService;
|
||||
import cn.bunny.services.utils.system.RoleUtil;
|
||||
import cn.bunny.services.service.system.helper.role.RoleHelper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import jakarta.annotation.Resource;
|
||||
|
@ -33,7 +33,7 @@ public class RolePermissionServiceImpl extends ServiceImpl<RolePermissionMapper,
|
|||
private UserMapper userMapper;
|
||||
|
||||
@Resource
|
||||
private RoleUtil roleUtil;
|
||||
private RoleHelper roleHelper;
|
||||
|
||||
@Resource
|
||||
private UserRoleMapper userRoleMapper;
|
||||
|
@ -86,6 +86,6 @@ public class RolePermissionServiceImpl extends ServiceImpl<RolePermissionMapper,
|
|||
|
||||
// 更新Redis中用户信息
|
||||
List<Long> userIds = adminUsers.stream().map(AdminUser::getId).toList();
|
||||
roleUtil.updateUserRedisInfo(userIds);
|
||||
roleHelper.updateUserRedisInfo(userIds);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@ import cn.bunny.services.mapper.system.RolePermissionMapper;
|
|||
import cn.bunny.services.mapper.system.RouterRoleMapper;
|
||||
import cn.bunny.services.mapper.system.UserRoleMapper;
|
||||
import cn.bunny.services.service.system.RoleService;
|
||||
import cn.bunny.services.service.system.helper.role.RoleUpdatedEvent;
|
||||
import cn.bunny.services.utils.FileUtil;
|
||||
import cn.bunny.services.utils.system.RoleUtil;
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
|
@ -28,6 +28,7 @@ import jakarta.validation.Valid;
|
|||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
@ -67,7 +68,7 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements Ro
|
|||
private RouterRoleMapper routerRoleMapper;
|
||||
|
||||
@Resource
|
||||
private RoleUtil roleUtil;
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
/**
|
||||
* * 角色 服务实现类
|
||||
|
@ -203,7 +204,10 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements Ro
|
|||
// 找到所有和当前更新角色相同的用户,并更新Redis中用户信息
|
||||
List<Long> userIds = userRoleMapper.selectList(Wrappers.<UserRole>lambdaQuery().eq(UserRole::getRoleId, dto.getId()))
|
||||
.stream().map(UserRole::getUserId).toList();
|
||||
roleUtil.updateUserRedisInfo(userIds);
|
||||
// TODO 1
|
||||
// roleUtil.updateUserRedisInfo(userIds);
|
||||
// 发布角色更新事件
|
||||
eventPublisher.publishEvent(new RoleUpdatedEvent(this, dto.getId()));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package cn.bunny.services.service.system.impl;
|
||||
|
||||
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
|
||||
import cn.bunny.services.domain.system.system.dto.router.RouterAddDto;
|
||||
import cn.bunny.services.domain.system.system.dto.router.RouterUpdateDto;
|
||||
import cn.bunny.services.domain.system.system.entity.router.Router;
|
||||
|
@ -10,13 +11,12 @@ import cn.bunny.services.domain.system.system.views.ViewRouterRole;
|
|||
import cn.bunny.services.domain.system.system.vo.router.RouterManageVo;
|
||||
import cn.bunny.services.domain.system.system.vo.router.RouterVo;
|
||||
import cn.bunny.services.domain.system.system.vo.router.WebUserRouterVo;
|
||||
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
|
||||
import cn.bunny.services.exception.AuthCustomerException;
|
||||
import cn.bunny.services.mapper.system.RolePermissionMapper;
|
||||
import cn.bunny.services.mapper.system.RouterMapper;
|
||||
import cn.bunny.services.mapper.system.RouterRoleMapper;
|
||||
import cn.bunny.services.service.system.RouterService;
|
||||
import cn.bunny.services.utils.system.RouterUtil;
|
||||
import cn.bunny.services.service.system.helper.RouterHelper;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
@ -48,7 +48,7 @@ public class RouterServiceImpl extends ServiceImpl<RouterMapper, Router> impleme
|
|||
private RouterRoleMapper routerRoleMapper;
|
||||
|
||||
@Resource
|
||||
private RouterUtil routerUtil;
|
||||
private RouterHelper routerHelper;
|
||||
|
||||
@Resource
|
||||
private RolePermissionMapper rolePermissionMapper;
|
||||
|
@ -75,13 +75,13 @@ public class RouterServiceImpl extends ServiceImpl<RouterMapper, Router> impleme
|
|||
.collect(Collectors.groupingBy(ViewRolePermission::getRoleId, Collectors.toList()));
|
||||
|
||||
// 整理web用户所能看到的路由列表,并检查当前用户是否是admin
|
||||
List<WebUserRouterVo> webUserRouterVoList = routerUtil.getWebUserRouterVos(routerList, routerRoleList, rolePermissionList);
|
||||
List<WebUserRouterVo> webUserRouterVoList = routerHelper.getWebUserRouterVos(routerList, routerRoleList, rolePermissionList);
|
||||
|
||||
// 添加 admin 管理路由权限
|
||||
webUserRouterVoList.forEach(routerVo -> {
|
||||
// 递归添加路由节点
|
||||
if (routerVo.getParentId() == 0) {
|
||||
routerVo.setChildren(routerUtil.buildTreeSetChildren(routerVo.getId(), webUserRouterVoList));
|
||||
routerVo.setChildren(routerHelper.buildTreeSetChildren(routerVo.getId(), webUserRouterVoList));
|
||||
voList.add(routerVo);
|
||||
}
|
||||
});
|
||||
|
@ -141,7 +141,7 @@ public class RouterServiceImpl extends ServiceImpl<RouterMapper, Router> impleme
|
|||
|
||||
// 将数据提出role 和 power 存储到数据库
|
||||
Long id = router.getId();
|
||||
routerUtil.insertRouterRoleAndPermission(meta, id);
|
||||
routerHelper.insertRouterRoleAndPermission(meta, id);
|
||||
|
||||
// 添加路由
|
||||
save(router);
|
||||
|
@ -169,7 +169,7 @@ public class RouterServiceImpl extends ServiceImpl<RouterMapper, Router> impleme
|
|||
routerRoleMapper.deleteBatchIdsByRouterIds(List.of(id));
|
||||
|
||||
// 将数据提出role 和 power 存储到数据库
|
||||
routerUtil.insertRouterRoleAndPermission(meta, id);
|
||||
routerHelper.insertRouterRoleAndPermission(meta, id);
|
||||
|
||||
// 更新路由信息
|
||||
updateById(router);
|
||||
|
|
|
@ -15,6 +15,7 @@ import cn.bunny.services.domain.system.system.vo.user.RefreshTokenVo;
|
|||
import cn.bunny.services.exception.AuthCustomerException;
|
||||
import cn.bunny.services.mapper.configuration.EmailTemplateMapper;
|
||||
import cn.bunny.services.mapper.system.UserMapper;
|
||||
import cn.bunny.services.service.configuration.helper.email.ConcreteSenderEmailTemplate;
|
||||
import cn.bunny.services.service.system.UserLoginService;
|
||||
import cn.bunny.services.service.system.helper.UserLoginHelper;
|
||||
import cn.bunny.services.service.system.helper.login.DefaultLoginStrategy;
|
||||
|
@ -23,7 +24,6 @@ import cn.bunny.services.service.system.helper.login.LoginContext;
|
|||
import cn.bunny.services.service.system.helper.login.LoginStrategy;
|
||||
import cn.bunny.services.utils.IpUtil;
|
||||
import cn.bunny.services.utils.JwtTokenUtil;
|
||||
import cn.bunny.services.utils.email.ConcreteSenderEmailTemplate;
|
||||
import cn.hutool.captcha.CaptchaUtil;
|
||||
import cn.hutool.captcha.CircleCaptcha;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
|
@ -103,9 +103,25 @@ public class UserLoginServiceImpl extends ServiceImpl<UserMapper, AdminUser> imp
|
|||
}
|
||||
|
||||
/**
|
||||
* 登录发送邮件验证码
|
||||
* 发送登录邮件验证码
|
||||
*
|
||||
* @param email 邮箱
|
||||
* <p>完整处理流程:</p>
|
||||
* <ol>
|
||||
* <li><b>查询模板</b>:从数据库获取默认的验证码邮件模板</li>
|
||||
* <li><b>生成验证码</b>:创建4位数字验证码</li>
|
||||
* <li><b>模板处理</b>:替换模板中的动态变量(系统名称、验证码等)</li>
|
||||
* <li><b>发送邮件</b>:通过邮件服务发送处理后的模板</li>
|
||||
* <li><b>缓存验证码</b>:将验证码存入Redis 有效期 xxx</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param email 接收邮箱地址(不可为空)
|
||||
* <ul>
|
||||
* <li>未找到默认邮件模板</li>
|
||||
* <li>邮件发送失败</li>
|
||||
* <li>Redis操作异常</li>
|
||||
* </ul>
|
||||
* @see EmailTemplateEnums#VERIFICATION_CODE 验证码模板类型枚举
|
||||
* @see RedisUserConstant Redis键和过期时间常量
|
||||
*/
|
||||
@Override
|
||||
public void sendLoginEmail(@NotNull String email) {
|
||||
|
@ -131,7 +147,8 @@ public class UserLoginServiceImpl extends ServiceImpl<UserMapper, AdminUser> imp
|
|||
concreteSenderEmailTemplate.sendEmailTemplate(email, emailTemplate, hashMap);
|
||||
|
||||
// 在Redis中存储验证码
|
||||
redisTemplate.opsForValue().set(RedisUserConstant.getAdminUserEmailCodePrefix(email), emailCode, RedisUserConstant.REDIS_EXPIRATION_TIME, TimeUnit.MINUTES);
|
||||
String emailCodePrefix = RedisUserConstant.getAdminUserEmailCodePrefix(email);
|
||||
redisTemplate.opsForValue().set(emailCodePrefix, emailCode, RedisUserConstant.REDIS_EXPIRATION_TIME, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -102,9 +102,28 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implemen
|
|||
}
|
||||
|
||||
/**
|
||||
* 强制退出
|
||||
* 管理员强制用户下线
|
||||
*
|
||||
* @param id 用户id
|
||||
* <p><b>功能说明</b>:管理员强制指定用户退出登录状态,并记录操作日志</p>
|
||||
*
|
||||
* <p><b>处理流程</b>:</p>
|
||||
* <ol>
|
||||
* <li>参数校验:检查用户ID是否为空</li>
|
||||
* <li>查询用户信息:根据ID获取用户实体</li>
|
||||
* <li>记录操作日志:保存强制下线记录到用户登录日志表</li>
|
||||
* <li>清除登录状态:删除Redis中的用户登录信息</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p><b>注意事项</b>:</p>
|
||||
* <ul>
|
||||
* <li>会中断用户当前会话,无需用户确认</li>
|
||||
* <li>操作会记录到用户登录日志,用于审计追踪</li>
|
||||
* <li>Redis键使用用户名作为唯一标识</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param id 用户ID(不可为空)
|
||||
* @see RedisUserConstant#getAdminLoginInfoPrefix Redis键生成规则
|
||||
* @see UserConstant#FORCE_LOGOUT 强制下线类型常量
|
||||
*/
|
||||
@Override
|
||||
public void forcedOfflineByAdmin(Long id) {
|
||||
|
@ -117,6 +136,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implemen
|
|||
// 将用户登录保存在用户登录日志表中
|
||||
UserLoginLog userLoginLog = new UserLoginLog();
|
||||
BeanUtils.copyProperties(adminUser, userLoginLog);
|
||||
userLoginLog.setId(null);
|
||||
userLoginLog.setUserId(adminUser.getId());
|
||||
userLoginLog.setType(UserConstant.FORCE_LOGOUT);
|
||||
userLoginLogMapper.insert(userLoginLog);
|
||||
|
|
Loading…
Reference in New Issue