Merge branch 'dev'

This commit is contained in:
bunny 2025-05-06 16:23:58 +08:00
commit f3ea4ffdfa
120 changed files with 1241 additions and 2170 deletions

View File

@ -26,6 +26,7 @@ ENTRYPOINT ["java","-jar","/home/server/app.jar"]
#暴露 8000 端口
EXPOSE 8000
EXPOSE 7070
# 生产环境
# mvn clean package -Pprod -DskipTests

View File

@ -3,7 +3,7 @@ package cn.bunny.services.controller.configuration;
import cn.bunny.services.domain.system.configuration.dto.WebConfigurationDto;
import cn.bunny.services.domain.system.configuration.entity.WebConfiguration;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.service.configuration.ConfigurationService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;

View File

@ -1,13 +1,13 @@
package cn.bunny.services.controller.configuration;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.system.email.dto.EmailTemplateAddDto;
import cn.bunny.services.domain.system.email.dto.EmailTemplateDto;
import cn.bunny.services.domain.system.email.dto.EmailTemplateUpdateDto;
import cn.bunny.services.domain.system.email.entity.EmailTemplate;
import cn.bunny.services.domain.system.email.vo.EmailTemplateVo;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.service.configuration.EmailTemplateService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
@ -71,7 +71,7 @@ public class EmailTemplateController {
}
@Operation(summary = "全部邮件类型列表", description = "获取全部邮件类型列表", tags = "emailTemplate::query")
@GetMapping("public")
@GetMapping("private")
public Result<List<Map<String, String>>> getEmailTypeList() {
List<Map<String, String>> list = emailTemplateService.getEmailTypeList();
return Result.success(list);

View File

@ -7,7 +7,7 @@ import cn.bunny.services.domain.system.email.entity.EmailUsers;
import cn.bunny.services.domain.system.email.vo.EmailUsersVo;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.service.configuration.EmailUsersService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;

View File

@ -8,7 +8,7 @@ import cn.bunny.services.domain.system.i18n.entity.I18n;
import cn.bunny.services.domain.system.i18n.vo.I18nVo;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.service.configuration.I18nService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;

View File

@ -5,7 +5,7 @@ import cn.bunny.services.domain.system.i18n.dto.I18nTypeDto;
import cn.bunny.services.domain.system.i18n.dto.I18nTypeUpdateDto;
import cn.bunny.services.domain.system.i18n.vo.I18nTypeVo;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.service.configuration.I18nTypeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;

View File

@ -7,7 +7,7 @@ import cn.bunny.services.domain.system.menuIcon.entity.MenuIcon;
import cn.bunny.services.domain.system.menuIcon.vo.MenuIconVo;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.service.configuration.MenuIconService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;

View File

@ -5,7 +5,7 @@ import cn.bunny.services.domain.system.log.entity.ScheduleExecuteLog;
import cn.bunny.services.domain.system.log.vo.ScheduleExecuteLogVo;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.service.log.ScheduleExecuteLogService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;

View File

@ -6,7 +6,7 @@ import cn.bunny.services.domain.system.log.vo.UserLoginLogLocalVo;
import cn.bunny.services.domain.system.log.vo.UserLoginLogVo;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.service.log.UserLoginLogService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;

View File

@ -9,7 +9,7 @@ import cn.bunny.services.domain.system.message.vo.MessageReceivedWithUserVo;
import cn.bunny.services.domain.system.message.vo.MessageVo;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.service.message.MessageService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;

View File

@ -8,7 +8,7 @@ import cn.bunny.services.domain.system.message.vo.MessageReceivedWithMessageVo;
import cn.bunny.services.domain.system.message.vo.MessageUserVo;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.service.message.MessageReceivedService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;

View File

@ -7,7 +7,7 @@ import cn.bunny.services.domain.system.message.entity.MessageType;
import cn.bunny.services.domain.system.message.vo.MessageTypeVo;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.service.message.MessageTypeService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;

View File

@ -3,7 +3,7 @@ package cn.bunny.services.controller.schedule;
import cn.bunny.services.aop.scanner.QuartzSchedulersScanner;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.domain.system.quartz.dto.SchedulersAddDto;
import cn.bunny.services.domain.system.quartz.dto.SchedulersDto;
import cn.bunny.services.domain.system.quartz.dto.SchedulersUpdateDto;

View File

@ -7,7 +7,7 @@ import cn.bunny.services.domain.system.quartz.entity.SchedulersGroup;
import cn.bunny.services.domain.system.quartz.vo.SchedulersGroupVo;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.service.schedule.SchedulersGroupService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;

View File

@ -7,7 +7,7 @@ import cn.bunny.services.domain.system.system.entity.Dept;
import cn.bunny.services.domain.system.system.vo.DeptVo;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.service.system.DeptService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;

View File

@ -10,7 +10,7 @@ import cn.bunny.services.domain.system.files.vo.FileInfoVo;
import cn.bunny.services.domain.system.files.vo.FilesVo;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.service.system.FilesService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;

View File

@ -4,7 +4,7 @@ import cn.bunny.services.aop.scanner.ControllerApiPermissionScanner;
import cn.bunny.services.domain.common.model.dto.scanner.ScannerControllerInfoVo;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.domain.system.system.dto.power.PermissionAddDto;
import cn.bunny.services.domain.system.system.dto.power.PermissionDto;
import cn.bunny.services.domain.system.system.dto.power.PermissionUpdateBatchByParentIdDto;

View File

@ -7,7 +7,7 @@ import cn.bunny.services.domain.system.system.entity.Role;
import cn.bunny.services.domain.system.system.vo.RoleVo;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.service.system.RoleService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;

View File

@ -5,7 +5,7 @@ import cn.bunny.services.domain.system.system.dto.router.RouterUpdateDto;
import cn.bunny.services.domain.system.system.vo.router.RouterManageVo;
import cn.bunny.services.domain.system.system.vo.router.WebUserRouterVo;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.service.system.RouterService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;

View File

@ -1,9 +1,8 @@
package cn.bunny.services.controller.system;
import cn.bunny.services.domain.common.model.vo.LoginVo;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.system.system.dto.user.AdminUserAddDto;
import cn.bunny.services.domain.system.system.dto.user.AdminUserDto;
import cn.bunny.services.domain.system.system.dto.user.AdminUserUpdateDto;
@ -91,13 +90,13 @@ public class UserController {
@Operation(summary = "已登录用户", description = "查询缓存中已登录用户", tags = "user::query")
@GetMapping("getCacheUserPage/{page}/{limit}")
public Result<PageResult<LoginVo>> getCacheUserPage(
public Result<PageResult<UserVo>> getCacheUserPage(
@Parameter(name = "page", description = "当前页", required = true)
@PathVariable("page") Integer page,
@Parameter(name = "limit", description = "每页记录数", required = true)
@PathVariable("limit") Integer limit) {
Page<AdminUser> pageParams = new Page<>(page, limit);
PageResult<LoginVo> pageResult = userService.getCacheUserPage(pageParams);
PageResult<UserVo> pageResult = userService.getCacheUserPage(pageParams);
return Result.success(pageResult);
}

View File

@ -3,7 +3,7 @@ package cn.bunny.services.controller.system;
import cn.bunny.services.context.BaseContext;
import cn.bunny.services.domain.common.model.vo.LoginVo;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.domain.system.system.dto.user.AdminUserUpdateByLocalUserDto;
import cn.bunny.services.domain.system.system.dto.user.LoginDto;
import cn.bunny.services.domain.system.system.dto.user.RefreshTokenDto;

View File

@ -1,6 +1,8 @@
server:
port: 8000
tomcat:
threads:
max: 1000
spring:
profiles:
active: @profiles.active@

View File

@ -1,10 +0,0 @@
public abstract class AbstractPermissionCheckHandler {
private AbstractPermissionCheckHandler abstractPermissionCheckHandler;
public AbstractPermissionCheckHandler(AbstractPermissionCheckHandler abstractPermissionCheckHandler) {
this.abstractPermissionCheckHandler = abstractPermissionCheckHandler;
}
abstract protected void checkPermission(String requestUrl);
}

View File

@ -1,27 +0,0 @@
package cn.bunny.services.config;
import cn.bunny.services.utils.TokenUtilsTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
// @TestConfiguration
// public class WebConfig {
// @Value("${server.port}")
// private String port;
//
// @Autowired
// private TokenUtilsTest tokenUtils;
//
// @Bean
// public RestTemplate restTemplate(RestTemplateBuilder builder) {
// String token = tokenUtils.getToken();
// return builder.rootUri("http://localhost:" + port)
// .defaultHeader("token", token)
// .defaultHeader("Content-Type", "application/json")
// .build();
// }
// }

View File

@ -1,37 +0,0 @@
package cn.bunny.services.controller;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.chrome.ChromeDriver;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
@SpringBootTest
@AutoConfigureMockMvc
public class LoginTest {
private ChromeDriver chromeDriver;
@BeforeEach
void setUpMockMvc() {
chromeDriver = new ChromeDriver();
}
@AfterEach
void tearDown() {
chromeDriver.quit();
}
// 测试登录页面
@Test
// @WithMockUser(username = "Administrator", password = "admin123", roles = "admin")
// @WithUserDetails("Administrator")
void testLogin() throws InterruptedException {
chromeDriver.get("http://localhost:7000/");
TimeUnit.MINUTES.sleep(100);
chromeDriver.manage().timeouts().implicitlyWait(Duration.of(1000L, TimeUnit.SECONDS.toChronoUnit()));
}
}

View File

@ -1,56 +0,0 @@
package cn.bunny.services.controller;
import cn.bunny.services.aop.scanner.ControllerApiPermissionScanner;
import cn.bunny.services.domain.common.model.dto.scanner.ScannerControllerInfoVo;
import cn.bunny.services.domain.system.system.entity.Permission;
import cn.bunny.services.service.system.PermissionService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class TestControllerTest {
@Autowired
private PermissionService permissionService;
@Test
void test1() {
List<ScannerControllerInfoVo> list = ControllerApiPermissionScanner.getSystemApiInfoList();
list.forEach(parent -> {
String parentPath = parent.getPath();
String powerCode = parentPath.replace("/api/", "").replace("/**", "");
powerCode = "user::" + powerCode;
Permission permission = new Permission();
permission.setParentId(0L);
permission.setPowerCode(powerCode);
permission.setPowerName(parent.getSummary());
permission.setRequestUrl(parentPath);
permissionService.saveOrUpdate(permission);
// System.out.println(permission);
List<Permission> permissionList = parent.getChildren().stream()
.map(children -> {
Permission childrenPermission = new Permission();
childrenPermission.setParentId(permission.getId());
childrenPermission.setPowerName(children.getSummary());
if (!children.getPowerCodes().isEmpty()) {
String ChildrenPowerCode = children.getPowerCodes().get(0);
childrenPermission.setPowerCode(ChildrenPowerCode);
}
String childrenPath = children.getPath();
childrenPermission.setRequestUrl(childrenPath);
childrenPermission.setRequestMethod(children.getHttpMethod());
return childrenPermission;
})
.toList();
// System.out.println(JSON.toJSONString(permissionList));
permissionService.saveOrUpdateBatch(permissionList);
});
}
}

View File

@ -1,163 +0,0 @@
package cn.bunny.services.controller.configuration;
import cn.bunny.services.domain.system.configuration.dto.WebConfigurationDto;
import cn.bunny.services.domain.system.configuration.entity.WebConfiguration;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.utils.TokenUtilsTest;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import java.util.concurrent.atomic.AtomicReference;
@SpringBootTest
@WebAppConfiguration
class ConfigurationControllerTest {
private static final String prefix = "/api/config";
@Autowired
private WebApplicationContext webApplicationContext;
@Autowired
private TokenUtilsTest tokenUtils;
private MockMvc mockMvc;
private String token;
@BeforeEach
void setUpMockMvc() {
token = tokenUtils.getToken();
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
}
@Test
void webConfig() throws Exception {
mockMvc.perform(MockMvcRequestBuilders
.get(prefix + "/noAuth/webConfig")
.header("token", token))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(result -> {
MockHttpServletResponse response = result.getResponse();
String contentAsString = response.getContentAsString();
WebConfiguration webConfiguration = JSON.parseObject(contentAsString, WebConfiguration.class);
if (!webConfiguration.getTitle().equals("BunnyAdmin")) {
throw new Exception();
}
System.out.println(webConfiguration);
});
}
@Test
void getWebConfig() throws Exception {
mockMvc.perform(MockMvcRequestBuilders
.request(HttpMethod.GET, prefix + "/getWebConfig")
.header("token", token))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(result -> {
MockHttpServletResponse response = result.getResponse();
String contentAsString = response.getContentAsString();
Result<WebConfiguration> webConfigurationResult = JSON.parseObject(contentAsString, new TypeReference<>() {
});
if (!webConfigurationResult.getCode().equals(200)) {
throw new Exception();
}
if (!webConfigurationResult.getData().getShowModel().equals("smart")) {
throw new Exception();
}
System.out.println(contentAsString);
});
}
@Test
void updateWebConfiguration() throws Exception {
AtomicReference<WebConfigurationDto> webConfigurationDto = new AtomicReference<>();
String testTitle = "修改的之";
// 获取原本的配置信息
mockMvc.perform(MockMvcRequestBuilders
.request(HttpMethod.GET, prefix + "/getWebConfig")
.header("token", token))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(result -> {
MockHttpServletResponse response = result.getResponse();
String contentAsString = response.getContentAsString();
System.out.println(contentAsString);
Result<WebConfiguration> webConfigurationResult = JSON.parseObject(contentAsString, new TypeReference<>() {
});
WebConfigurationDto dto = new WebConfigurationDto();
BeanUtils.copyProperties(webConfigurationResult.getData(), dto);
webConfigurationDto.set(dto);
});
// 修改原本的测试内容
webConfigurationDto.get().setTitle(testTitle);
// 测试修改方法
mockMvc.perform(MockMvcRequestBuilders.put(prefix + "/updateWebConfiguration")
.contentType(MediaType.APPLICATION_JSON)
.content(JSON.toJSONString(webConfigurationDto.get()))
.header("token", token))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(result -> {
String contentAsString = result.getResponse().getContentAsString();
System.out.println(contentAsString);
Result<String> stringResult = JSON.parseObject(contentAsString, new TypeReference<>() {
});
if (!stringResult.getCode().equals(200)) {
throw new Exception();
}
});
// 验证是否修改成功
mockMvc.perform(MockMvcRequestBuilders
.request(HttpMethod.GET, prefix + "/getWebConfig")
.header("token", token))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(result -> {
MockHttpServletResponse response = result.getResponse();
String contentAsString = response.getContentAsString();
Result<WebConfiguration> webConfigurationResult = JSON.parseObject(contentAsString, new TypeReference<>() {
});
if (!webConfigurationResult.getCode().equals(200)) {
throw new Exception();
}
if (!webConfigurationResult.getData().getTitle().equals(testTitle)) {
throw new Exception();
}
System.out.println(contentAsString);
});
}
}

View File

@ -1,215 +0,0 @@
package cn.bunny.services.controller.configuration;
import cn.bunny.services.domain.system.email.dto.EmailTemplateAddDto;
import cn.bunny.services.domain.system.email.dto.EmailTemplateUpdateDto;
import cn.bunny.services.domain.system.email.vo.EmailTemplateVo;
import cn.bunny.services.domain.common.enums.EmailTemplateEnums;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.utils.TokenUtilsTest;
import cn.hutool.crypto.digest.MD5;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.util.UriComponentsBuilder;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@SpringBootTest
@WebAppConfiguration
class EmailTemplateControllerTest {
private static final String prefix = "/api/emailTemplate";
private String token;
private MockMvc mockMvc;
@Autowired
private WebApplicationContext webApplicationContext;
@Autowired
private TokenUtilsTest tokenUtilsTest;
@Autowired
private RestTemplate restTemplate;
@BeforeEach
void setUp() {
token = tokenUtilsTest.getToken();
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
}
@AfterEach
void tearDown() {
}
@Test
void getEmailTemplatePage() throws Exception {
String api = prefix + "/getEmailTemplateList/1/10";
mockMvc.perform(MockMvcRequestBuilders
.get(api)
.header("token", token)
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("type", "code"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(result -> {
String contentAsString = result.getResponse().getContentAsString();
JSONObject jsonObject = JSONObject.parseObject(contentAsString);
if (jsonObject == null) {
throw new Exception(contentAsString);
}
System.out.println(jsonObject);
});
}
@Test
void getEmailTypeList() throws Exception {
String api = prefix + "/getEmailTypes";
mockMvc.perform(MockMvcRequestBuilders.get(api).header("token", token))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(result -> {
String contentAsString = result.getResponse().getContentAsString();
JSONObject jsonObject = JSONObject.parseObject(contentAsString);
if (jsonObject == null) {
throw new Exception(contentAsString);
}
System.out.println(jsonObject);
});
}
@Test
void addEmailTemplate() throws Exception {
String api = prefix + "/addEmailTemplate";
EmailTemplateAddDto dto = EmailTemplateAddDto.builder()
.emailUser(2L)
.body("哈哈哈")
.templateName("测试")
.subject("test")
.type(EmailTemplateEnums.NOTIFICATION.getType())
.isDefault(false)
.build();
mockMvc.perform(MockMvcRequestBuilders
.post(api)
.header("token", token)
.content(JSONObject.toJSONString(dto))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(result -> {
String contentAsString = result.getResponse().getContentAsString();
JSONObject jsonObject = JSONObject.parseObject(contentAsString);
System.out.println(jsonObject);
});
getEmailTemplatePage();
}
@Test
void updateEmailTemplate() throws Exception {
String api = prefix + "/updateEmailTemplate";
String restTemplateApi = prefix + "/getEmailTemplateList/1/10";
String url = UriComponentsBuilder.fromUriString(restTemplateApi)
.queryParam("subject", "test")
.build()
.toUriString();
Result<PageResult<EmailTemplateVo>> result = restTemplate.exchange(
url,
HttpMethod.GET,
new HttpEntity<>(null),
new ParameterizedTypeReference<Result<PageResult<EmailTemplateVo>>>() {
}
).getBody();
if (result == null) throw new Exception();
if (!result.getCode().equals(200)) throw new Exception(result.getMessage());
if (result.getData().getList().isEmpty()) throw new Exception("没有测试数据");
EmailTemplateVo emailTemplateVo = result.getData().getList().get(0);
EmailTemplateUpdateDto emailTemplateUpdateDto = new EmailTemplateUpdateDto();
BeanUtils.copyProperties(emailTemplateVo, emailTemplateUpdateDto);
System.out.println(emailTemplateUpdateDto);
emailTemplateUpdateDto.setBody(MD5.create().digestHex16(LocalDateTime.now().toString()));
mockMvc.perform(MockMvcRequestBuilders.put(api)
.header("token", token)
.content(JSON.toJSONString(emailTemplateUpdateDto))
.contentType(MediaType.APPLICATION_JSON)
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(result1 -> {
String contentAsString = result1.getResponse().getContentAsString();
var pageResultResult = JSONObject.parseObject(contentAsString, new TypeReference<Result<PageResult<EmailTemplateVo>>>() {
});
if (pageResultResult == null) throw new Exception(contentAsString);
if (!pageResultResult.getCode().equals(200)) throw new Exception(pageResultResult.getMessage());
System.out.println(pageResultResult);
});
result = restTemplate.exchange(
url,
HttpMethod.GET,
new HttpEntity<>(null),
new ParameterizedTypeReference<Result<PageResult<EmailTemplateVo>>>() {
}
).getBody();
if (result == null) throw new Exception();
if (!result.getCode().equals(200)) throw new Exception(result.getMessage());
emailTemplateVo = result.getData().getList().get(0);
if (!emailTemplateVo.getBody().equals(emailTemplateUpdateDto.getBody())) {
throw new Exception(emailTemplateUpdateDto.getBody());
}
}
@Test
void deleteEmailTemplate() throws Exception {
String api = prefix + "/deleteEmailTemplate";
List<Long> ids = new ArrayList<>();
ids.add(1905180657917157378L);
mockMvc.perform(MockMvcRequestBuilders
.delete(api)
.header("token", token)
.contentType(MediaType.APPLICATION_JSON)
.content(JSON.toJSONString(ids)))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(result -> {
String contentAsString = result.getResponse().getContentAsString();
JSONObject jsonObject = JSONObject.parseObject(contentAsString);
System.out.println(jsonObject);
});
}
}

View File

@ -1,179 +0,0 @@
package cn.bunny.services.controller.log;
import cn.bunny.services.domain.system.log.dto.UserLoginLogDto;
import cn.bunny.services.domain.system.log.entity.UserLoginLog;
import cn.bunny.services.domain.system.log.vo.UserLoginLogLocalVo;
import cn.bunny.services.domain.system.log.vo.UserLoginLogVo;
import cn.bunny.services.domain.common.model.vo.result.PageResult;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.service.log.UserLoginLogService;
import cn.bunny.services.utils.TokenUtilsTest;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.WebApplicationContext;
import java.util.List;
import java.util.Map;
@Slf4j
@SpringBootTest
@WebAppConfiguration
class UserLoginLogControllerTest {
private static final String prefix = "/api/userLoginLog";
private WebTestClient testClient;
@Autowired
private WebApplicationContext webApplicationContext;
@Autowired
private UserLoginLogService userLoginLogService;
@Autowired
private TokenUtilsTest tokenUtils;
private MockMvc mockMvc;
private String token;
@Autowired
private RestTemplate restTemplate;
@BeforeEach
void setUp() {
token = tokenUtils.getToken();
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
testClient = WebTestClient.bindToController(new UserLoginLogController()).build();
}
@AfterEach
void tearDown() {
}
@Test
void getUserLoginLogPage() throws Exception {
UserLoginLogDto dto = UserLoginLogDto.builder()
.username("bunny")
.build();
mockMvc.perform(MockMvcRequestBuilders.get(prefix + "/getUserLoginLogList/1/10")
.contentType(MediaType.APPLICATION_JSON)
.content(JSON.toJSONString(dto))
.header("token", token))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(result -> {
MockHttpServletResponse response = result.getResponse();
String contentAsString = response.getContentAsString();
System.out.println(contentAsString);
});
}
@Test
void getUserLoginLogPage2() {
UserLoginLogDto dto = UserLoginLogDto.builder()
.username("bunny")
.build();
Map<String, String> params = JSON.parseObject(JSON.toJSONString(dto), new TypeReference<>() {
});
// 发送请求
ResponseEntity<Result<PageResult<UserLoginLogVo>>> response = restTemplate.exchange(
prefix + "/getUserLoginLogList/1/10",
HttpMethod.GET,
new HttpEntity<>(params),
new ParameterizedTypeReference<>() {
}
);
Result<PageResult<UserLoginLogVo>> body = response.getBody();
System.out.println(JSON.toJSONString(body));
}
@Test
void getUserLoginLogPageByUser() throws Exception {
String api = prefix + "/noManage/getUserLoginLogListByLocalUser/1/10";
mockMvc.perform(MockMvcRequestBuilders.get(api)
.header("token", token))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(result -> {
MockHttpServletResponse response = result.getResponse();
String contentAsString = response.getContentAsString();
System.out.println(contentAsString);
});
}
@Test
void getUserLoginLogPageByUser2() {
String api = prefix + "/noManage/getUserLoginLogListByLocalUser/1/10";
testClient.get()
.uri(api)
.header("token", token)
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk()
.expectBody(new ParameterizedTypeReference<Result<PageResult<UserLoginLogLocalVo>>>() {
})
.consumeWith(result -> {
Result<PageResult<UserLoginLogLocalVo>> responseBody = result.getResponseBody();
System.out.println(JSON.toJSONString(responseBody));
});
}
@Test
void deleteUserLoginLog() throws Exception {
String api = prefix + "/deleteUserLoginLog";
Page<UserLoginLog> page = new Page<>(1, 10);
List<UserLoginLog> deleteBeforeList = userLoginLogService.list(page);
List<Long> ids = deleteBeforeList.stream().map(UserLoginLog::getId).limit(4).toList();
List<Long> deleteBeforeIds = deleteBeforeList.stream().map(UserLoginLog::getId).toList();
log.info("要删除的ids: {}", ids);
log.info("删除前ids数据{}", deleteBeforeIds);
mockMvc.perform(MockMvcRequestBuilders
.delete(api)
.header("token", token)
.contentType(MediaType.APPLICATION_JSON)
.content(JSON.toJSONString(ids)))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(result -> {
MockHttpServletResponse response = result.getResponse();
String contentAsString = response.getContentAsString();
System.out.println(contentAsString);
});
deleteBeforeList = userLoginLogService.list(page);
deleteBeforeIds = deleteBeforeList.stream().map(UserLoginLog::getId).toList();
log.info("要删除的ids: {}", ids);
log.info("删除前ids数据{}", deleteBeforeIds);
}
}

View File

@ -1,58 +0,0 @@
package cn.bunny.services.controller.system;
import cn.bunny.services.domain.common.constant.RedisUserConstant;
import com.alibaba.fastjson2.JSONObject;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class UserControllerTest {
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Test
void test() {
// Set<String> keys = redisTemplate.keys("admin::login_info::*");
// for (String key : keys) {
// System.out.println(key);
// }
Map<String, Object> adminLoginInfoWithScan = getAdminLoginInfoWithScan();
JSONObject adminLoginInfo = new JSONObject(adminLoginInfoWithScan);
System.out.println(adminLoginInfo);
}
public Map<String, Object> getAdminLoginInfoWithScan() {
String pattern = "admin::login_info::*";
Map<String, Object> result = new HashMap<>();
// 使用scan命令迭代查找
ScanOptions options = ScanOptions.scanOptions().match(pattern).count(100).build();
Cursor<String> cursor = redisTemplate.scan(options);
while (cursor.hasNext()) {
String key = cursor.next();
Object value = redisTemplate.opsForValue().get(key);
result.put(key, value);
}
try {
cursor.close();
} catch (Exception e) {
// 处理异常
}
return result;
}
}

View File

@ -1,25 +0,0 @@
package cn.bunny.services.utils;
import cn.bunny.services.domain.common.model.vo.LoginVo;
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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class TokenUtilsTest {
@Autowired
private UserLoginHelper userUtil;
@Autowired
private UserMapper userMapper;
public String getToken() {
AdminUser adminUser = userMapper.selectOne(Wrappers.<AdminUser>lambdaQuery().eq(AdminUser::getUsername, "Administrator"));
adminUser.setPassword("admin123");
LoginVo loginVo = userUtil.buildLoginUserVo(adminUser, 7);
return loginVo.getToken();
}
}

View File

@ -1,55 +0,0 @@
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 com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class I18nServiceImplTest extends ServiceImpl<I18nMapper, I18n> {
@Test
void downloadI18nByExcel() {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try (ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) {
// 查找默认语言内容
List<I18n> i18nList = list();
Map<String, List<I18nExcel>> hashMap = i18nList.stream()
.collect(Collectors.groupingBy(
I18n::getTypeName,
Collectors.mapping((I18n i18n) -> {
String keyName = i18n.getKeyName();
String translation = i18n.getTranslation();
return I18nExcel.builder().keyName(keyName).translation(translation).build();
}, Collectors.toList())
));
hashMap.forEach((key, value) -> {
// EasyExcel.write(key + ".xlsx", I18nExcel.class).sheet(key).doWrite(value);
try {
ZipEntry zipEntry = new ZipEntry(key + ".xlsx");
zipOutputStream.putNextEntry(zipEntry);
// 直接写入到ZipOutputStream
EasyExcel.write(zipOutputStream, I18nExcel.class).sheet(key).doWrite(value);
zipOutputStream.closeEntry();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,38 @@
package impl;
import cn.bunny.services.AuthServiceApplication;
import cn.bunny.services.core.template.PermissionTreeProcessor;
import cn.bunny.services.domain.common.model.dto.excel.PermissionExcel;
import cn.bunny.services.domain.system.system.entity.Permission;
import cn.bunny.services.mapper.system.PermissionMapper;
import com.alibaba.fastjson2.JSON;
import org.junit.jupiter.api.Test;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest(classes = AuthServiceApplication.class)
class PermissionServiceImplTest {
@Autowired
private PermissionMapper permissionMapper;
@Test
void exportPermission() {
List<Permission> permissionList = permissionMapper.selectList(null);
List<PermissionExcel> permissionExcelList = permissionList.stream().map(permission -> {
PermissionExcel permissionExcel = new PermissionExcel();
BeanUtils.copyProperties(permission, permissionExcel);
return permissionExcel;
}).toList();
PermissionTreeProcessor permissionTreeProcessor = new PermissionTreeProcessor();
List<PermissionExcel> buildTree = permissionTreeProcessor.process(permissionExcelList);
System.out.println(JSON.toJSONString(buildTree));
}
}

View File

@ -1,33 +0,0 @@
package system;
import cn.bunny.services.controller.system.UserController;
import cn.bunny.services.domain.common.constant.RedisUserConstant;
import cn.bunny.services.service.system.impl.UserServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisConnectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.web.WebAppConfiguration;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest(classes = UserServiceImpl.class)
class UserServiceTest {
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Test
void test() {
String prefix = RedisUserConstant.getAdminUserEmailCodePrefix("");
Set<String> keys = redisTemplate.keys(prefix);
for (String key : keys) {
System.out.println(key);
}
}
}

View File

@ -127,5 +127,11 @@
<artifactId>ip2region</artifactId>
<version>2.6.5</version>
</dependency>
<!-- Could not write JSON: Java 8 date/time type `java.time.LocalDateTime` not supported by default -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -18,7 +18,6 @@ import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@ -42,13 +41,14 @@ public class RedisConfiguration {
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
// 设置key序列化为string
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
// 设置value序列化为JSON使用GenericJackson2JsonRedisSerializer替换默认序列化
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setValueSerializer(jsonRedisSerializer());
redisTemplate.setHashValueSerializer(jsonRedisSerializer());
// 开启Redis事务
redisTemplate.setEnableTransactionSupport(true);

View File

@ -12,15 +12,57 @@ public class RedisUserConstant {
public static final Integer Cookie_EXPIRATION_TIME = 5 * 60 * 60;// cookies 过期时间 5 分钟
public static final String WEB_CONFIG_KEY = "webConfig::platformConfig";// web配置
private static final String ADMIN_LOGIN_INFO_PREFIX = "admin::login_info::";
private static final String ADMIN_EMAIL_CODE_PREFIX = "admin::email_code::";
/* 用户登录前缀 */
private static final String USER_LOGIN_INFO_PREFIX = "user::login_info::";
/* 用户邮箱验证码前缀 */
private static final String USER_EMAIL_CODE_PREFIX = "user::email_code::";
/* 用户角色前缀 */
private static final String USER_ROLES_CODE_PREFIX = "user::roles::";
/* 用户权限前缀 */
private static final String USER_PERMISSION_CODE_PREFIX = "user::permission::";
/**
* 用户登录前缀
*
* @param user 用户名信息
* @return 格式化后缓存前缀
*/
public static String getUserLoginInfoPrefix(String user) {
return USER_LOGIN_INFO_PREFIX + user;
public static String getAdminLoginInfoPrefix(String adminUser) {
return ADMIN_LOGIN_INFO_PREFIX + adminUser;
}
public static String getAdminUserEmailCodePrefix(String adminUser) {
return ADMIN_EMAIL_CODE_PREFIX + adminUser;
/**
* 用户邮箱前缀
*
* @param user 用户信息
* @return 邮箱验证码前缀
*/
public static String getUserEmailCodePrefix(String user) {
return USER_EMAIL_CODE_PREFIX + user;
}
/**
* 用户角色前缀
*
* @param user 用户信息
* @return 格式化后用户角色前缀
*/
public static String getUserRolesCodePrefix(String user) {
return USER_ROLES_CODE_PREFIX + user;
}
/**
* 用户权限前缀
*
* @param user 用户信息
* @return 格式化后用户权限前缀
*/
public static String getUserPermissionCodePrefix(String user) {
return USER_PERMISSION_CODE_PREFIX + user;
}
}

View File

@ -1,4 +1,4 @@
package cn.bunny.services.domain.common.model.vo.result;
package cn.bunny.services.domain.common.enums;
import lombok.Getter;
@ -53,7 +53,8 @@ public enum ResultCodeEnum {
LOGIN_AUTH(208, "请先登陆"),
AUTHENTICATION_EXPIRED(208, "身份验证过期"),
SESSION_EXPIRATION(208, "会话过期"),
FAIL_NO_ACCESS_DENIED_USER_LOCKED(208, "该账户已封禁"),
// 209
THE_SAME_USER_HAS_LOGGED_IN(209, "相同用户已登录"),
@ -71,7 +72,6 @@ public enum ResultCodeEnum {
FAIL_NO_ACCESS_DENIED(403, "无权访问"),
FAIL_NO_ACCESS_DENIED_USER_OFFLINE(403, "用户强制下线"),
TOKEN_PARSING_FAILED(403, "token解析失败"),
FAIL_NO_ACCESS_DENIED_USER_LOCKED(403, "该账户已封禁"),
// 系统错误 500
UNKNOWN_EXCEPTION(500, "服务异常"),

View File

@ -1,5 +1,6 @@
package cn.bunny.services.domain.common.model.vo.result;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

View File

@ -1,6 +1,6 @@
package cn.bunny.services.exception;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

View File

@ -2,7 +2,7 @@ package cn.bunny.services.exception;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.context.BaseContext;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.MyBatisSystemException;

View File

@ -2,7 +2,7 @@ package cn.bunny.services.minio;
import cn.bunny.services.domain.common.constant.MinioConstant;
import cn.bunny.services.domain.common.model.dto.minio.MinioUploadFileInfo;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.exception.AuthCustomerException;
import io.minio.*;
import io.minio.messages.DeleteError;

View File

@ -0,0 +1,18 @@
package cn.bunny.services.processor;
import java.util.List;
/* 构建树型结构模板处理器 */
public abstract class TreeProcessor<T> {
public final List<T> process(List<T> list) {
List<T> roots = findRoots(list);
for (T root : roots) {
buildChildren(root, list);
}
return roots;
}
protected abstract List<T> findRoots(List<T> list);
protected abstract void buildChildren(T parent, List<T> list);
}

View File

@ -1,6 +1,6 @@
package cn.bunny.services.utils;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.exception.AuthCustomerException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;

View File

@ -1,14 +0,0 @@
import org.junit.jupiter.api.Test;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MinioTest {
@Test
void test() {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM-dd");
String date = formatter.format(new Date());
System.out.println(date);
}
}

View File

@ -21,9 +21,9 @@ public interface RolePermissionMapper extends BaseMapper<RolePermission> {
/**
* * 根据权限id列表删除角色权限相关
*
* @param powerIds 权限id列表
* @param permissionIds 权限id列表
*/
void deleteBatchPowerIds(List<Long> powerIds);
void deleteBatchPowerIds(List<Long> permissionIds);
/**
* * 根据角色id删除角色权限
@ -38,7 +38,7 @@ public interface RolePermissionMapper extends BaseMapper<RolePermission> {
* @param roleId 角色id
* @return 已选择的权限列表
*/
List<RolePermission> selectPowerListByRoleId(Long roleId);
List<RolePermission> selectRolePermissionListByRoleId(Long roleId);
/**
* 查看所有角色关联的权限
@ -46,4 +46,12 @@ public interface RolePermissionMapper extends BaseMapper<RolePermission> {
* @return 角色权限关系视图
*/
List<ViewRolePermission> viewRolePowerWithAll();
/**
* 根据权限id列表查询角色和权限
*
* @param permissionIds 权限id列表
* @return List<RolePermission>
*/
List<RolePermission> selectRolePermissionListByPermissionIds(List<Long> permissionIds);
}

View File

@ -31,4 +31,12 @@ public interface UserRoleMapper extends BaseMapper<UserRole> {
*/
void deleteBatchIdsByRoleIds(List<Long> roleIds);
/**
* 根据角色id列表查询
*
* @param ids 角色id列表
* @return {@link List<UserRole>}
*/
List<UserRole> selectListByRoleIds(List<Long> ids);
}

View File

@ -53,15 +53,11 @@
<!-- 根据用户id查询当前用户所有权限 -->
<select id="selectListByUserId" resultType="cn.bunny.services.domain.system.system.entity.Permission">
SELECT p.*
FROM sys_user u,
sys_user_role ur,
sys_role_permission rp,
sys_permission p
WHERE u.id = ur.user_id
AND u.is_deleted = 0
AND ur.role_id = rp.role_id
AND rp.power_id = p.id
AND u.id = #{userId}
FROM sys_permission p
INNER JOIN sys_role_permission rp ON p.id = rp.power_id
INNER JOIN sys_user_role ur ON rp.role_id = ur.role_id
INNER JOIN sys_user u ON ur.user_id = u.id AND u.is_deleted = 0
WHERE u.id = #{userId}
</select>
</mapper>

View File

@ -43,12 +43,10 @@
<!-- 根据用户id查询当前用户所有角色 -->
<select id="selectListByUserId" resultType="cn.bunny.services.domain.system.system.entity.Role">
SELECT r.*
FROM sys_user u,
sys_role r,
sys_user_role ur
WHERE u.id = ur.user_id
AND u.is_deleted = 0
AND r.id = ur.role_id
FROM sys_user u
INNER JOIN sys_user_role ur ON u.id = ur.user_id
INNER JOIN sys_role r ON r.id = ur.role_id
WHERE u.is_deleted = 0
AND ur.user_id = #{userId}
</select>

View File

@ -24,7 +24,7 @@
delete
from sys_role_permission
where power_id in
<foreach collection="powerIds" item="id" open="(" close=")" separator=",">
<foreach collection="permissionIds" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
@ -40,7 +40,8 @@
</delete>
<!-- 根据角色id获取权限内容 -->
<select id="selectPowerListByRoleId" resultType="cn.bunny.services.domain.system.system.entity.RolePermission">
<select id="selectRolePermissionListByRoleId"
resultType="cn.bunny.services.domain.system.system.entity.RolePermission">
select *
from sys_role_permission
where role_id = #{roleId}
@ -61,4 +62,14 @@
LEFT JOIN sys_permission permission ON rp.power_id = permission.id
</select>
<!-- 根据权限id列表查询角色和权限 -->
<select id="selectRolePermissionListByPermissionIds"
resultType="cn.bunny.services.domain.system.system.entity.RolePermission">
SELECT * FROM
sys_role_permission WHERE power_id IN
<foreach collection="permissionIds" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
</mapper>

View File

@ -39,4 +39,12 @@
</foreach>
</delete>
<!-- 根据角色id列表查询 -->
<select id="selectListByRoleIds" resultType="cn.bunny.services.domain.system.system.entity.UserRole">
SELECT * FROM sys_user_role WHERE role_id IN
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
</mapper>

View File

@ -1,99 +0,0 @@
package cn.bunny;
import cn.bunny.core.TypeConvertCore;
import cn.bunny.dao.entity.ColumnMetaData;
import cn.bunny.dao.entity.TableMetaData;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
@SpringBootTest
public class JDBCTest {
DatabaseMetaData metaData;
private final DataSource dataSource;
public JDBCTest(DataSource dataSource) {
this.dataSource = dataSource;
}
@BeforeEach
public void setUp() throws Exception {
try (Connection connection = dataSource.getConnection()) {
metaData = connection.getMetaData();
}
}
@Test
void testComment() throws SQLException {
String tableName = "sys_i18n";
TableMetaData tableMetaData;
ResultSet tables = metaData.getTables(null, null, tableName, new String[]{"TABLE"});
// 获取表的注释信息
if (tables.next()) {
String remarks = tables.getString("REMARKS");
String tableCat = tables.getString("TABLE_CAT");
tableMetaData = TableMetaData.builder()
.tableName(tableName)
.comment(remarks)
.tableCat(tableCat)
.build();
System.out.println(tableMetaData);
}
}
@Test
void testAllTableComment() throws SQLException {
ResultSet tables = metaData.getTables(null, null, "%", new String[]{"TABLE"});
List<TableMetaData> list = new ArrayList<>();
while (tables.next()) {
String tableName = tables.getString("TABLE_NAME");
String remarks = tables.getString("REMARKS");
String tableCat = tables.getString("TABLE_CAT");
TableMetaData tableMetaData = TableMetaData.builder()
.tableName(tableName).comment(remarks)
.tableCat(tableCat)
.build();
list.add(tableMetaData);
}
System.out.println(list);
}
@Test
void testColumnInfo() throws SQLException {
List<ColumnMetaData> columns = new ArrayList<>();
try (ResultSet columnsRs = metaData.getColumns(null, null, "sys_i18n", null)) {
while (columnsRs.next()) {
ColumnMetaData column = new ColumnMetaData();
column.setColumnName(columnsRs.getString("COLUMN_NAME"));
column.setLowercaseName(TypeConvertCore.convertToCamelCase(column.getColumnName()));
column.setJdbcType(columnsRs.getString("TYPE_NAME"));
column.setJavaType(TypeConvertCore.convertToJavaType(column.getJdbcType()));
column.setComment(columnsRs.getString("REMARKS"));
columns.add(column);
System.out.println(column);
}
}
System.out.println(columns);
}
}

View File

@ -1,105 +0,0 @@
package cn.bunny;
import cn.bunny.core.TypeConvertCore;
import cn.bunny.dao.entity.ColumnMetaData;
import cn.bunny.dao.entity.TableMetaData;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.create.table.CreateTable;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SqlParserTest {
@Test
public void test() throws JSQLParserException {
String sql = """
CREATE TABLE `sys_files` (
`id` bigint NOT NULL COMMENT '文件的唯一标识符自动递增',
`filename` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '文件的名称',
`filepath` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '文件在服务器上的存储路径',
`file_size` int NOT NULL COMMENT '文件的大小以字节为单位',
`file_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '文件的MIME类型',
`download_count` int NULL DEFAULT 0 COMMENT '下载数量',
`create_user` bigint NOT NULL COMMENT '创建用户',
`update_user` bigint NULL DEFAULT NULL COMMENT '操作用户',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录文件最后修改的时间戳',
`is_deleted` tinyint(1) UNSIGNED ZEROFILL NOT NULL DEFAULT 0 COMMENT '文件是否被删除',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_filename`(`filename` ASC) USING BTREE COMMENT '索引文件名',
INDEX `idx_filepath`(`filepath` ASC) USING BTREE COMMENT '索引文件路径',
INDEX `idx_file_type`(`file_type` ASC) USING BTREE COMMENT '索引文件类型',
INDEX `idx_update_user`(`update_user` ASC) USING BTREE COMMENT '索引创更新用户',
INDEX `idx_create_user`(`create_user` ASC) USING BTREE COMMENT '索引创建用户',
INDEX `idx_user`(`update_user` ASC, `create_user` ASC) USING BTREE COMMENT '索引创建用户和更新用户',
INDEX `idx_time`(`update_time` ASC, `create_time` ASC) USING BTREE COMMENT '索引创建时间和更新时间'
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '系统文件表' ROW_FORMAT = DYNAMIC;
""";
TableMetaData tableInfo = new TableMetaData();
// 解析sql
Statement statement = CCJSqlParserUtil.parse(sql);
if (!(statement instanceof CreateTable createTable)) {
throw new IllegalArgumentException("Not a CREATE TABLE statement");
}
// 设置表基本信息
tableInfo.setTableName(createTable.getTable().getName());
tableInfo.setTableType("TABLE");
String tableOptionsStrings = String.join(" ", createTable.getTableOptionsStrings());
// 注释信息
Pattern pattern = Pattern.compile("COMMENT\\s*=\\s*'(.*?)'", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(tableOptionsStrings);
if (matcher.find()) {
tableInfo.setComment(matcher.group(1));
}
// 解析列信息
List<ColumnMetaData> columnMetaData = createTable.getColumnDefinitions()
.stream().map(column -> {
// 列信息
ColumnMetaData columnInfo = new ColumnMetaData();
// 列名称
columnInfo.setColumnName(column.getColumnName());
// 设置 JDBC 类型
String dataType = column.getColDataType().getDataType();
columnInfo.setJdbcType(dataType);
// 设置 Java 类型
String javaType = TypeConvertCore.convertToJavaType(dataType.contains("varchar") ? "varchar" : dataType);
columnInfo.setJavaType(javaType);
// 设置 JavaScript 类型
columnInfo.setJavascriptType(StringUtils.uncapitalize(javaType));
// 列字段转成 下划线 -> 小驼峰
columnInfo.setLowercaseName(TypeConvertCore.convertToCamelCase(column.getColumnName()));
// 列字段转成 下划线 -> 大驼峰名称
columnInfo.setUppercaseName(TypeConvertCore.convertToCamelCase(column.getColumnName(), true));
// 解析注释
List<String> columnSpecs = column.getColumnSpecs();
String columnSpecsString = String.join(" ", columnSpecs);
Matcher columnSpecsStringMatcher = Pattern.compile("COMMENT\\s*'(.*?)'", Pattern.CASE_INSENSITIVE).matcher(columnSpecsString);
if (columnSpecsStringMatcher.find()) {
columnInfo.setComment(columnSpecsStringMatcher.group(1));
}
return columnInfo;
}).toList();
System.out.println(tableInfo);
System.out.println("----------------------------------------------------------------------------------------");
System.out.println(columnMetaData);
}
}

View File

@ -1,43 +0,0 @@
package cn.bunny;
import cn.bunny.core.TypeConvertCore;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.introspection.CaseFormatUtils;
import org.junit.jupiter.api.Test;
public class StringFormatTest {
@Test
void test1() {
System.out.println(CaseFormatUtils.toCamelCase("user_login" ));
System.out.println(CaseFormatUtils.toCamelCase("userLogin" ));
System.out.println(CaseFormatUtils.toCamelCase("UserLogin" ));
System.out.println("--------------------------------" );
System.out.println(StringUtils.lowerCase("user_login" ));
System.out.println(StringUtils.lowerCase("userLogin" ));
System.out.println(StringUtils.lowerCase("UserLogin" ));
System.out.println("--------------------------------" );
System.out.println(StringUtils.upperCase("user_login" ));
System.out.println(StringUtils.upperCase("userLogin" ));
System.out.println(StringUtils.upperCase("UserLogin" ));
}
@Test
void test2() {
System.out.println(TypeConvertCore.convertToCamelCase("user_login_A" ));
System.out.println(TypeConvertCore.convertToCamelCase("User_Login_A" ));
System.out.println(TypeConvertCore.convertToCamelCase("userLoginA" ));
System.out.println(TypeConvertCore.convertToCamelCase("UserLoginA" ));
System.out.println("--------------------------------" );
System.out.println(TypeConvertCore.convertToCamelCase("i18n_type_A" , true));
System.out.println(TypeConvertCore.convertToCamelCase("User_Login_A" , true));
System.out.println(TypeConvertCore.convertToCamelCase("userLoginA" , true));
System.out.println(TypeConvertCore.convertToCamelCase("UserLoginA" , true));
}
}

View File

@ -1,14 +0,0 @@
package cn.bunny;
import cn.hutool.crypto.digest.MD5;
import org.junit.jupiter.api.Test;
public class TimeTest {
@Test
void timeTest() {
long currentTimeMillis = System.currentTimeMillis();
String digestHex = MD5.create().digestHex(currentTimeMillis + "");
System.out.println(currentTimeMillis);
System.out.println(digestHex);
}
}

View File

@ -1,50 +0,0 @@
package cn.bunny.service.impl;
import cn.bunny.core.ResourceFileCore;
import cn.bunny.dao.vo.VmsPathVo;
import com.alibaba.fastjson2.JSON;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
class VmsServiceImplTest {
@Test
void vmsResourcePathList() throws IOException, URISyntaxException {
List<String> vmsFiles = ResourceFileCore.getAbsoluteFiles("vms");
System.out.println(vmsFiles);
System.out.println("--------------------------------------------------------------");
List<String> vmsRelativeFiles = ResourceFileCore.getRelativeFiles("vms");
System.out.println(vmsRelativeFiles);
System.out.println("--------------------------集合对象模式------------------------------------");
Map<String, List<VmsPathVo>> map = vmsRelativeFiles.stream().map(vmFile -> {
String[] filepathList = vmFile.split("/");
String filename = filepathList[filepathList.length - 1].replace(".vm", "");
return VmsPathVo.builder().name(vmFile).label(filename).type(filepathList[0]).build();
}).collect(Collectors.groupingBy(VmsPathVo::getType));
System.out.println(JSON.toJSONString(map));
System.out.println("----------------------------二维数组格式----------------------------------");
List<List<VmsPathVo>> listMap = vmsRelativeFiles.stream().map(vmFile -> {
String[] filepathList = vmFile.split("/");
String filename = filepathList[filepathList.length - 1].replace(".vm", "");
return VmsPathVo.builder().name(vmFile).label(filename).type(filepathList[0]).build();
})
.collect(Collectors.groupingBy(VmsPathVo::getType))
.values().stream().toList();
System.out.println(JSON.toJSONString(listMap));
}
}

View File

@ -1,75 +0,0 @@
package cn.bunny.utils;
import cn.bunny.dao.entity.TableMetaData;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
@SpringBootTest
class DatabaseInfoCoreTest {
String tableName = "sys_i18n";
@Autowired
private DataSource dataSource;
@Test
void testTableInfoMetaData() {
TableMetaData tableMetaData;
try (Connection connection = dataSource.getConnection()) {
DatabaseMetaData metaData = connection.getMetaData();
ResultSet tables = metaData.getTables(null, null, tableName, new String[]{"TABLE"});
// 获取表的注释信息
if (tables.next()) {
String remarks = tables.getString("REMARKS");
String tableCat = tables.getString("TABLE_CAT");
String tableType = tables.getString("TABLE_TYPE");
tableMetaData = TableMetaData.builder()
.tableName(tableName)
.comment(remarks)
.tableCat(tableCat)
.tableType(tableType)
.build();
} else {
throw new RuntimeException("数据表不存在");
}
System.out.println(tableMetaData);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@SneakyThrows
@Test
void getDbTableList() {
String dbName = "auth_admin";
try (Connection connection = dataSource.getConnection()) {
DatabaseMetaData metaData = connection.getMetaData();
ResultSet tables = metaData.getTables(dbName, null, "%", new String[]{"TABLE"});
List<String> list = new ArrayList<>();
while (tables.next()) {
dbName = tables.getString("TABLE_NAME");
list.add(dbName);
}
System.out.println(list);
}
}
}

View File

@ -43,7 +43,7 @@
<velocity-tools.version>3.1</velocity-tools.version>
<HikariCP.version>6.2.1</HikariCP.version>
<dynamic.datasource.version>4.3.1</dynamic.datasource.version>
<jackson-dataType.version>2.12.3</jackson-dataType.version>
<jackson-dataType.version>2.19.0</jackson-dataType.version>
<quartz-scheduler.version>2.3.2</quartz-scheduler.version>
</properties>

View File

@ -91,10 +91,5 @@
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,6 +1,6 @@
package cn.bunny.services.aop.scanner;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.exception.AuthCustomerException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;

View File

@ -0,0 +1,58 @@
package cn.bunny.services.core.cache;
import cn.bunny.services.domain.common.constant.RedisUserConstant;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import jakarta.annotation.Resource;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class EmailCacheService {
@Resource
private RedisTemplate<String, Object> redisTemplate;
/**
* 存储邮箱验证码
*
* @param email 邮箱
* @param emailCode 邮箱验证码
*/
public void buildEmailCodeCache(@NotNull String email, String emailCode) {
// 在Redis中存储验证码
String emailCodePrefix = RedisUserConstant.getUserEmailCodePrefix(email);
redisTemplate.opsForValue().set(emailCodePrefix, emailCode, RedisUserConstant.REDIS_EXPIRATION_TIME, TimeUnit.MINUTES);
}
/**
* 获取邮箱验证码
*
* @param email 邮箱
* @return 邮箱验证码
*/
public String getEmailCode(@NotNull String email) {
String userEmailCodePrefix = RedisUserConstant.getUserEmailCodePrefix(email);
Object emailCode = redisTemplate.opsForValue().get(userEmailCodePrefix);
if (emailCode == null) {
throw new UsernameNotFoundException(ResultCodeEnum.EMAIL_CODE_EMPTY.getMessage());
}
return emailCode.toString();
}
/**
* 清除邮箱验证码
*
* @param email 邮箱
*/
public void deleteEmailCodeCache(String email) {
String emailCodePrefix = RedisUserConstant.getUserEmailCodePrefix(email);
redisTemplate.delete(emailCodePrefix);
}
}

View File

@ -1,4 +1,4 @@
package cn.bunny.services.redis;
package cn.bunny.services.core.cache;
import cn.bunny.services.domain.common.constant.RedisUserConstant;
import jakarta.annotation.Resource;
@ -26,7 +26,7 @@ public class RedisService {
*/
@NotNull
public List<String> scannerRedisKeyByPage(long pageNum, long pageSize) {
String prefix = RedisUserConstant.getAdminLoginInfoPrefix("*");
String prefix = RedisUserConstant.getUserLoginInfoPrefix("*");
List<String> keys = new ArrayList<>();
ScanOptions scanOptions = ScanOptions.scanOptions()

View File

@ -0,0 +1,110 @@
package cn.bunny.services.core.cache;
import cn.bunny.services.domain.common.constant.RedisUserConstant;
import cn.bunny.services.domain.system.system.entity.Permission;
import cn.bunny.services.domain.system.system.entity.Role;
import cn.bunny.services.mapper.system.PermissionMapper;
import cn.bunny.services.mapper.system.RoleMapper;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@Service
public class UserAuthorizationCacheService {
@Resource
private PermissionMapper permissionMapper;
@Resource
private RoleMapper roleMapper;
@Resource
private RedisTemplate<String, Object> redisTemplate;
/**
* 根据用户ID和用户名获取角色列表缓存优先
*
* <p><b>执行逻辑</b></p>
* <ol>
* <li>尝试从Redis缓存获取用户角色使用用户名作为缓存key</li>
* <li>若缓存未命中则从数据库查询并写入缓存有效期1天</li>
* <li>若缓存命中反序列化缓存数据返回</li>
* </ol>
*
* @param userId 用户ID数据库查询用
* @param username 用户名缓存key生成用
* @return 用户角色列表
*/
public List<Role> getRolesByUser(Long userId, String username) {
// 角色列表
List<Role> roleList;
// 尝试从缓存中获取当前用户角色
String userRolesCodePrefix = RedisUserConstant.getUserRolesCodePrefix(username);
Object object = redisTemplate.opsForValue().get(userRolesCodePrefix);
// 为空查询数据库并将信息存入Redis
if (Objects.isNull(object)) {
roleList = roleMapper.selectListByUserId(userId);
redisTemplate.opsForValue().set(userRolesCodePrefix, roleList, 1, TimeUnit.DAYS);
}
// 从缓存中得到当前用户的角色列表
else {
String jsonString = JSON.toJSONString(object);
TypeReference<List<Role>> reference = new TypeReference<>() {
};
List<Role> list = JSON.parseObject(jsonString, reference);
// 防止 list 为空报错
roleList = list != null ? list : new ArrayList<>();
}
return roleList;
}
/**
* 根据用户ID和用户名获取权限列表缓存优先
*
* <p><b>执行逻辑</b></p>
* <ol>
* <li>尝试从Redis缓存获取用户权限使用用户名作为缓存key</li>
* <li>若缓存未命中则从数据库查询并写入缓存有效期1天</li>
* <li>若缓存命中反序列化缓存数据返回</li>
* </ol>
*
* @param userId 用户ID数据库查询用
* @param username 用户名缓存key生成用
* @return 用户权限列表
*/
public List<Permission> getPermissionsByUser(Long userId, String username) {
// 权限列表
List<Permission> permissionList;
// 获取缓存中的用户权限
String userPermissionCodePrefix = RedisUserConstant.getUserPermissionCodePrefix(username);
Object object = redisTemplate.opsForValue().get(userPermissionCodePrefix);
// 为空查询数据库并将用户权限放在Redis中
if (Objects.isNull(object)) {
permissionList = permissionMapper.selectListByUserId(userId);
redisTemplate.opsForValue().set(userPermissionCodePrefix, permissionList, 1, TimeUnit.DAYS);
}
// 从缓存中得到当前用户权限
else {
String jsonString = JSON.toJSONString(object);
TypeReference<List<Permission>> reference = new TypeReference<>() {
};
List<Permission> list = JSON.parseObject(jsonString, reference);
// 防止 list 为空报错
permissionList = list != null ? list : new ArrayList<>();
}
return permissionList;
}
}

View File

@ -0,0 +1,33 @@
package cn.bunny.services.core.cache;
import cn.bunny.services.domain.common.constant.RedisUserConstant;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
@Component
public class UserCacheCleaner {
@Resource
private RedisTemplate<String, Object> redisTemplate;
public void cleanUserLoginCache(String username) {
String key = RedisUserConstant.getUserLoginInfoPrefix(username);
redisTemplate.delete(key);
}
public void cleanUserRoleCache(String username) {
String key = RedisUserConstant.getUserRolesCodePrefix(username);
redisTemplate.delete(key);
}
public void cleanUserPermissionCache(String username) {
String key = RedisUserConstant.getUserPermissionCodePrefix(username);
redisTemplate.delete(key);
}
public void cleanAllUserCache(String username) {
cleanUserLoginCache(username);
cleanUserRoleCache(username);
cleanUserPermissionCache(username);
}
}

View File

@ -0,0 +1,105 @@
package cn.bunny.services.core.cache;
import cn.bunny.services.core.utils.RoleHelper;
import cn.bunny.services.domain.common.constant.LocalDateTimeConstant;
import cn.bunny.services.domain.common.constant.RedisUserConstant;
import cn.bunny.services.domain.common.model.vo.LoginVo;
import cn.bunny.services.domain.system.system.entity.AdminUser;
import cn.bunny.services.domain.system.system.entity.Permission;
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.minio.MinioHelper;
import cn.bunny.services.utils.JwtTokenUtil;
import jakarta.annotation.Resource;
import lombok.Value;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Service
public class UserLoginVoBuilderCacheService {
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Resource
private MinioHelper minioHelper;
@Resource
private RoleMapper roleMapper;
@Resource
private PermissionMapper permissionMapper;
public LoginVo buildLoginUserVo(AdminUser user, long readMeDay) {
String username = user.getUsername();
Long userId = user.getId();
UserAuthInfo authInfo = getAuthInfo(userId, user);
// 设置用户返回对象
LoginVo loginVo = new LoginVo();
BeanUtils.copyProperties(user, loginVo);
loginVo.setPersonDescription(user.getSummary());
loginVo.setRoles(authInfo.getRoles());
loginVo.setPermissions(authInfo.getPermissions());
// 使用用户名创建token
String token = JwtTokenUtil.createToken(userId, username, (int) readMeDay);
loginVo.setToken(token);
loginVo.setRefreshToken(token);
loginVo.setReadMeDay(readMeDay);
// 计算过期时间并格式化返回
String expires = calculateExpires(readMeDay);
loginVo.setExpires(expires);
// 设置用户头像
String userAvatar = minioHelper.getUserAvatar(user.getAvatar());
loginVo.setAvatar(userAvatar);
String loginInfoPrefix = RedisUserConstant.getUserLoginInfoPrefix(username);
redisTemplate.opsForValue().set(loginInfoPrefix, loginVo, readMeDay, TimeUnit.DAYS);
return loginVo;
}
private UserAuthInfo getAuthInfo(Long userId, AdminUser user) {
// 用户角色
List<String> roles = new ArrayList<>(roleMapper.selectListByUserId(userId).stream().map(Role::getRoleCode).toList());
// 判断是否是 admin 如果是admin 赋予所有权限
List<String> permissions = new ArrayList<>();
boolean isAdmin = RoleHelper.checkAdmin(roles, permissions, user);
if (!isAdmin) {
permissions = permissionMapper.selectListByUserId(userId).stream()
.map(Permission::getPowerCode)
.toList();
}
// 为这两个去重在判断 checkAdmin 会重新赋值
permissions = permissions.stream().distinct().toList();
roles = roles.stream().distinct().toList();
return new UserAuthInfo(roles, permissions);
}
private String calculateExpires(long readMeDay) {
LocalDateTime localDateTime = LocalDateTime.now();
LocalDateTime plusDay = localDateTime.plusDays(readMeDay);
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(LocalDateTimeConstant.YYYY_MM_DD_HH_MM_SS_SLASH);
return plusDay.format(dateTimeFormatter);
}
@Value
private static class UserAuthInfo {
List<String> roles;
List<String> permissions;
}
}

View File

@ -0,0 +1,16 @@
package cn.bunny.services.core.event.event;
import lombok.Getter;
import lombok.Setter;
import org.springframework.context.ApplicationEvent;
@Getter
@Setter
public class ClearAllUserCacheEvent extends ApplicationEvent {
private final String key;
public ClearAllUserCacheEvent(Object source, String key) {
super(source);
this.key = key;
}
}

View File

@ -0,0 +1,18 @@
package cn.bunny.services.core.event.event;
import lombok.Getter;
import lombok.Setter;
import org.springframework.context.ApplicationEvent;
import java.util.List;
@Getter
@Setter
public class UpdateUserinfoByPermissionIdsEvent extends ApplicationEvent {
private final List<Long> permissionIds;
public UpdateUserinfoByPermissionIdsEvent(Object source, List<Long> permissionIds) {
super(source);
this.permissionIds = permissionIds;
}
}

View File

@ -0,0 +1,18 @@
package cn.bunny.services.core.event.event;
import lombok.Getter;
import lombok.Setter;
import org.springframework.context.ApplicationEvent;
import java.util.List;
@Getter
@Setter
public class UpdateUserinfoByRoleIdsEvent extends ApplicationEvent {
private final List<Long> roleIds;
public UpdateUserinfoByRoleIdsEvent(Object source, List<Long> roleIds) {
super(source);
this.roleIds = roleIds;
}
}

View File

@ -0,0 +1,19 @@
package cn.bunny.services.core.event.event;
import lombok.Getter;
import lombok.Setter;
import org.springframework.context.ApplicationEvent;
import java.util.List;
@Getter
@Setter
public class UpdateUserinfoByUserIdsEvent extends ApplicationEvent {
private final List<Long> userIds;
public UpdateUserinfoByUserIdsEvent(Object source, List<Long> userIds) {
super(source);
this.userIds = userIds;
}
}

View File

@ -1,4 +1,4 @@
package cn.bunny.services.excel;
package cn.bunny.services.core.event.listener.excel;
import cn.bunny.services.domain.common.model.dto.excel.I18nExcel;
import cn.bunny.services.domain.system.i18n.entity.I18n;

View File

@ -1,4 +1,4 @@
package cn.bunny.services.excel;
package cn.bunny.services.core.event.listener.excel;
import cn.bunny.services.domain.common.model.dto.excel.PermissionExcel;
import cn.bunny.services.domain.system.system.entity.Permission;

View File

@ -1,7 +1,7 @@
package cn.bunny.services.excel;
package cn.bunny.services.core.event.listener.excel;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.domain.common.model.dto.excel.RoleExcel;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.system.system.entity.Role;
import cn.bunny.services.exception.AuthCustomerException;
import cn.bunny.services.service.system.RoleService;

View File

@ -0,0 +1,34 @@
package cn.bunny.services.core.event.listener.user;
import cn.bunny.services.core.cache.UserCacheCleaner;
import cn.bunny.services.domain.common.constant.RedisUserConstant;
import cn.bunny.services.domain.system.system.entity.AdminUser;
import cn.bunny.services.mapper.system.UserMapper;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.function.Consumer;
@Component("AbstractUserInfoUpdateHandler")
public abstract class AbstractUserInfoUpdateHandler {
@Resource
protected UserMapper userMapper;
@Resource
protected UserCacheCleaner userCacheCleaner;
@Resource
private RedisTemplate<String, Object> redisTemplate;
public void processUserUpdate(List<Long> userIds, Consumer<AdminUser> postProcess) {
if (userIds.isEmpty()) return;
List<AdminUser> adminUsers = userMapper.selectBatchIds(userIds);
adminUsers.stream()
.filter(user -> redisTemplate.hasKey(RedisUserConstant.getUserLoginInfoPrefix(user.getUsername())))
.forEach(postProcess);
}
}

View File

@ -0,0 +1,68 @@
package cn.bunny.services.core.event.listener.user;
import cn.bunny.services.core.cache.UserLoginVoBuilderCacheService;
import cn.bunny.services.core.event.event.ClearAllUserCacheEvent;
import cn.bunny.services.core.event.event.UpdateUserinfoByPermissionIdsEvent;
import cn.bunny.services.core.event.event.UpdateUserinfoByRoleIdsEvent;
import cn.bunny.services.core.event.event.UpdateUserinfoByUserIdsEvent;
import cn.bunny.services.domain.common.constant.RedisUserConstant;
import cn.bunny.services.domain.system.system.entity.RolePermission;
import cn.bunny.services.domain.system.system.entity.UserRole;
import cn.bunny.services.mapper.system.RolePermissionMapper;
import cn.bunny.services.mapper.system.UserRoleMapper;
import jakarta.annotation.Resource;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.util.List;
@Component("UserinfoUpdateListener")
public class UserinfoUpdateListener extends AbstractUserInfoUpdateHandler {
@Resource
private UserLoginVoBuilderCacheService userLoginVoBuilderCacheService;
@Resource
private UserRoleMapper userRoleMapper;
@Resource
private RolePermissionMapper rolePermissionMapper;
/* 根据用户id更新用户信息重新生成LoginVo对象 */
@EventListener
public void handlerUpdateUserinfoByUserIds(UpdateUserinfoByUserIdsEvent event) {
List<Long> userIds = event.getUserIds();
processUserUpdate(userIds, user -> {
userCacheCleaner.cleanAllUserCache(user.getUsername());
userLoginVoBuilderCacheService.buildLoginUserVo(user, RedisUserConstant.REDIS_EXPIRATION_TIME);
});
}
/* 根据角色id更新用户信息重新生成LoginVo对象 */
@EventListener
public void handlerUserinfoUpdateByRoleId(UpdateUserinfoByRoleIdsEvent event) {
List<Long> roleIds = event.getRoleIds();
List<UserRole> userRoles = userRoleMapper.selectListByRoleIds(roleIds);
List<Long> userIds = userRoles.stream().map(UserRole::getUserId).toList();
UpdateUserinfoByUserIdsEvent userIdsEvent = new UpdateUserinfoByUserIdsEvent(event.getSource(), userIds);
handlerUpdateUserinfoByUserIds(userIdsEvent);
}
/* 根据角色id更新用户信息重新生成LoginVo对象 */
@EventListener
public void handlerUserinfoUpdateByPermissionId(UpdateUserinfoByPermissionIdsEvent event) {
List<Long> permissionIds = event.getPermissionIds();
List<RolePermission> rolePermissions = rolePermissionMapper.selectRolePermissionListByPermissionIds(permissionIds);
List<Long> roleIds = rolePermissions.stream().map(RolePermission::getRoleId).toList();
UpdateUserinfoByRoleIdsEvent roleIdsEvent = new UpdateUserinfoByRoleIdsEvent(event.getSource(), roleIds);
handlerUserinfoUpdateByRoleId(roleIdsEvent);
}
/* 清除用户登录、角色、权限所有缓存 */
@EventListener
public void handlerDeleteAllUserCache(ClearAllUserCacheEvent event) {
userCacheCleaner.cleanAllUserCache(event.getKey());
}
}

View File

@ -0,0 +1,35 @@
package cn.bunny.services.core.strategy.export;
import com.alibaba.excel.EasyExcel;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ExcelExportStrategy implements ExportStrategy<List<?>> {
private final Class<?> clazz;
private final String sheetName;
public ExcelExportStrategy(Class<?> clazz, String sheetName) {
this.clazz = clazz;
this.sheetName = sheetName;
}
@Override
public void export(List<?> data, ZipOutputStream zipOutputStream, String filename) {
try {
ByteArrayOutputStream excelOutputStream = new ByteArrayOutputStream();
EasyExcel.write(excelOutputStream, clazz).sheet(sheetName).doWrite(data);
// 将Excel写入到Zip中
ZipEntry zipEntry = new ZipEntry(filename);
zipOutputStream.putNextEntry(zipEntry);
zipOutputStream.write(excelOutputStream.toByteArray());
zipOutputStream.closeEntry();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,8 @@
package cn.bunny.services.core.strategy.export;
import java.io.IOException;
import java.util.zip.ZipOutputStream;
public interface ExportStrategy<T> {
void export(T data, ZipOutputStream zipOutputStream, String filename) throws IOException;
}

View File

@ -0,0 +1,22 @@
package cn.bunny.services.core.strategy.export;
import com.alibaba.fastjson2.JSON;
import java.nio.charset.StandardCharsets;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class JsonExportStrategy implements ExportStrategy<Object> {
@Override
public void export(Object data, ZipOutputStream zipOutputStream, String filename) {
try {
ZipEntry zipEntry = new ZipEntry(filename);
zipOutputStream.putNextEntry(zipEntry);
zipOutputStream.write(JSON.toJSONString(data).getBytes(StandardCharsets.UTF_8));
zipOutputStream.closeEntry();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -1,4 +1,4 @@
package cn.bunny.services.service.system.helper.login;
package cn.bunny.services.core.strategy.login;
import cn.bunny.services.domain.system.system.dto.user.LoginDto;
import cn.bunny.services.domain.system.system.entity.AdminUser;

View File

@ -1,23 +1,22 @@
package cn.bunny.services.service.system.helper.login;
package cn.bunny.services.core.strategy.login;
import cn.bunny.services.domain.common.constant.RedisUserConstant;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.core.cache.EmailCacheService;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.domain.system.system.dto.user.LoginDto;
import cn.bunny.services.domain.system.system.entity.AdminUser;
import cn.bunny.services.mapper.system.UserMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
* 邮箱登录策略
*/
public class EmailLoginStrategy implements LoginStrategy {
private final RedisTemplate<String, Object> redisTemplate;
private final EmailCacheService emailCacheService;
private final UserMapper userMapper;
public EmailLoginStrategy(RedisTemplate<String, Object> redisTemplate, UserMapper userMapper) {
this.redisTemplate = redisTemplate;
public EmailLoginStrategy(EmailCacheService emailCacheService, UserMapper userMapper) {
this.emailCacheService = emailCacheService;
this.userMapper = userMapper;
}
@ -38,13 +37,10 @@ public class EmailLoginStrategy implements LoginStrategy {
String emailCode = loginDto.getEmailCode().toLowerCase();
// 查找Redis中的验证码
Object redisEmailCode = redisTemplate.opsForValue().get(RedisUserConstant.getAdminUserEmailCodePrefix(username));
if (redisEmailCode == null) {
throw new UsernameNotFoundException(ResultCodeEnum.EMAIL_CODE_EMPTY.getMessage());
}
String redisEmailCode = emailCacheService.getEmailCode(username);
// 判断用户邮箱验证码是否和Redis中发送的验证码
if (!emailCode.equals(redisEmailCode.toString().toLowerCase())) {
if (!emailCode.equals(redisEmailCode.toLowerCase())) {
throw new UsernameNotFoundException(ResultCodeEnum.EMAIL_CODE_NOT_MATCHING.getMessage());
}
@ -63,8 +59,7 @@ public class EmailLoginStrategy implements LoginStrategy {
@Override
public void authenticateAfter(LoginDto loginDto, AdminUser adminUser) {
// 将Redis中验证码删除
String emailCodePrefix = RedisUserConstant.getAdminUserEmailCodePrefix(loginDto.getUsername());
redisTemplate.delete(emailCodePrefix);
emailCacheService.deleteEmailCodeCache(loginDto.getUsername());
}
}

View File

@ -1,4 +1,4 @@
package cn.bunny.services.service.system.helper.login;
package cn.bunny.services.core.strategy.login;
import cn.bunny.services.domain.system.system.dto.user.LoginDto;
import cn.bunny.services.domain.system.system.entity.AdminUser;

View File

@ -1,4 +1,4 @@
package cn.bunny.services.service.system.helper.login;
package cn.bunny.services.core.strategy.login;
import cn.bunny.services.domain.system.system.dto.user.LoginDto;
@ -21,7 +21,7 @@ public interface LoginStrategy {
* 登录完成后的内容
*
* @param loginDto 登录参数
* @param adminUser
* @param adminUser {@link AdminUser}
*/
void authenticateAfter(LoginDto loginDto, AdminUser adminUser);
}

View File

@ -0,0 +1,28 @@
package cn.bunny.services.core.template;
import cn.bunny.services.domain.common.model.dto.excel.PermissionExcel;
import cn.bunny.services.processor.TreeProcessor;
import java.util.List;
import java.util.stream.Collectors;
public class PermissionTreeProcessor extends TreeProcessor<PermissionExcel> {
@Override
public List<PermissionExcel> findRoots(List<PermissionExcel> list) {
return list.stream()
.filter(p -> p.getParentId() == null || p.getParentId() == 0)
.collect(Collectors.toList());
}
@Override
public void buildChildren(PermissionExcel parent, List<PermissionExcel> list) {
List<PermissionExcel> children = list.stream()
.filter(p -> parent.getId().equals(p.getParentId()))
.collect(Collectors.toList());
if (!children.isEmpty()) {
parent.setChildren(children);
children.forEach(child -> buildChildren(child, list));
}
}
}

View File

@ -1,9 +1,9 @@
package cn.bunny.services.service.configuration.helper.email;
package cn.bunny.services.core.template.email;
import cn.bunny.services.config.mail.MailSenderConfiguration;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.domain.common.model.dto.email.EmailSend;
import cn.bunny.services.domain.common.model.dto.email.EmailSendInit;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.system.email.entity.EmailTemplate;
import cn.bunny.services.domain.system.email.entity.EmailUsers;
import cn.bunny.services.exception.AuthCustomerException;

View File

@ -1,4 +1,4 @@
package cn.bunny.services.service.configuration.helper.email;
package cn.bunny.services.core.template.email;
import cn.bunny.services.domain.system.email.entity.EmailTemplate;
import cn.bunny.services.mapper.configuration.EmailTemplateMapper;

View File

@ -1,29 +1,13 @@
package cn.bunny.services.service.system.helper.role;
package cn.bunny.services.core.utils;
import cn.bunny.services.context.BaseContext;
import cn.bunny.services.domain.common.constant.RedisUserConstant;
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 jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class RoleHelper {
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Resource
private UserMapper userMapper;
@Resource
private UserLoginHelper userloginHelper;
/**
* 判断用户是否具有管理员权限
* <p>
@ -80,45 +64,4 @@ public class RoleHelper {
// 判断是否是 admin
return roleList.stream().anyMatch(permissionList::contains);
}
/**
* 批量更新Redis中用户权限信息
*
* <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;
// 批量查询用户
List<AdminUser> adminUsers = userMapper.selectBatchIds(userIds);
// 并行处理用户更新
adminUsers.parallelStream()
.filter(user -> redisTemplate.hasKey(RedisUserConstant.getAdminLoginInfoPrefix(user.getUsername())))
.forEach(user -> {
// 策略1: 更新用户权限信息
userloginHelper.buildLoginUserVo(user, RedisUserConstant.REDIS_EXPIRATION_TIME);
// 或者策略2: 强制用户下线
// redisTemplate.delete(RedisUserConstant.getAdminLoginInfoPrefix(user.getUsername()));
});
}
}

View File

@ -1,4 +1,4 @@
package cn.bunny.services.service.system.helper;
package cn.bunny.services.core.utils;
import cn.bunny.services.context.BaseContext;
import cn.bunny.services.domain.system.system.entity.RouterRole;
@ -8,7 +8,6 @@ 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.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;
@ -21,7 +20,7 @@ import java.util.*;
@Slf4j
@Component
public class RouterHelper {
public class RouterServiceHelper {
@Resource
private RouterRoleService routerRoleService;

View File

@ -1,6 +1,6 @@
package cn.bunny.services.security.config;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.domain.system.system.entity.AdminUser;
import cn.bunny.services.mapper.system.UserMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

View File

@ -1,6 +1,6 @@
package cn.bunny.services.security.exception;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

View File

@ -1,7 +1,7 @@
package cn.bunny.services.security.handelr;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import com.alibaba.fastjson2.JSON;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

View File

@ -1,7 +1,7 @@
package cn.bunny.services.security.handelr;
import cn.bunny.services.domain.common.model.vo.result.Result;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.security.exception.CustomAuthenticationException;
import cn.bunny.services.utils.ResponseUtil;
import jakarta.servlet.http.HttpServletRequest;

View File

@ -3,7 +3,7 @@ package cn.bunny.services.security.service;
import cn.bunny.services.context.BaseContext;
import cn.bunny.services.domain.common.model.dto.security.TokenInfo;
import cn.bunny.services.domain.common.model.vo.LoginVo;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.security.exception.CustomAuthenticationException;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;

View File

@ -1,12 +1,11 @@
package cn.bunny.services.security.service;
import cn.bunny.services.context.BaseContext;
import cn.bunny.services.core.cache.UserAuthorizationCacheService;
import cn.bunny.services.core.utils.RoleHelper;
import cn.bunny.services.domain.system.system.entity.Permission;
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.service.system.helper.role.RoleHelper;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@ -23,10 +22,7 @@ import java.util.Objects;
public class PermissionCheckService {
@Resource
private PermissionMapper permissionMapper;
@Resource
private RoleMapper roleMapper;
private UserAuthorizationCacheService authorizationCacheService;
/**
* 查询用户所属的角色信息
@ -38,7 +34,9 @@ public class PermissionCheckService {
// 根据用户ID查询角色数据
Long userId = BaseContext.getUserId();
List<Role> roleList = roleMapper.selectListByUserId(userId);
String username = BaseContext.getUsername();
// List<Role> roleList = roleMapper.selectListByUserId(userId);
List<Role> roleList = authorizationCacheService.getRolesByUser(userId, username);
// 角色代码
List<String> roleCodeList = roleList.stream().map(Role::getRoleCode).toList();
@ -54,7 +52,8 @@ public class PermissionCheckService {
}
// 根据角色列表查询权限信息
List<Permission> permissionList = permissionMapper.selectListByUserId(userId);
// List<Permission> permissionList = permissionMapper.selectListByUserId(userId);
List<Permission> permissionList = authorizationCacheService.getPermissionsByUser(userId, username);
// 判断是否与请求路径匹配
return permissionList.stream()

View File

@ -1,21 +1,21 @@
package cn.bunny.services.security.service;
import cn.bunny.services.domain.common.constant.RedisUserConstant;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.domain.common.model.dto.security.TokenInfo;
import cn.bunny.services.domain.common.model.vo.LoginVo;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.security.exception.CustomAuthenticationException;
import cn.bunny.services.utils.JwtTokenUtil;
import com.alibaba.fastjson2.JSON;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
/**
* 处理Token相关逻辑
*/
@Component
@Service
public class TokenValidationService {
@Resource
@ -39,7 +39,7 @@ public class TokenValidationService {
Long userId = JwtTokenUtil.getUserId(token);
// 查找 Redis
Object loginVoObject = redisTemplate.opsForValue().get(RedisUserConstant.getAdminLoginInfoPrefix(username));
Object loginVoObject = redisTemplate.opsForValue().get(RedisUserConstant.getUserLoginInfoPrefix(username));
LoginVo loginVo = JSON.parseObject(JSON.toJSONString(loginVoObject), LoginVo.class);
return TokenInfo.builder().userId(userId).username(username).token(token).loginVo(loginVo).build();

View File

@ -1,122 +0,0 @@
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;
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;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class I18nHelper {
/**
* 将国际化资源列表写入Excel并打包到ZIP输出流
*
* <p>处理流程</p>
* <ol>
* <li>按资源类型(typeName)分组 --- 多语言的类型英文?中文?</li>
* <li>每组数据生成单独多语言 key的Excel工作表</li>
* <li>将Excel文件写入ZIP输出流</li>
* </ol>
*
* @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()
.collect(Collectors.groupingBy(
I18n::getTypeName,
Collectors.mapping((I18n i18n) -> {
String keyName = i18n.getKeyName();
String translation = i18n.getTranslation();
return I18nExcel.builder().keyName(keyName).translation(translation).build();
}, Collectors.toList())
));
hashMap.forEach((key, value) -> {
try {
// 创建临时ByteArrayOutputStream
ByteArrayOutputStream excelOutputStream = new ByteArrayOutputStream();
ZipEntry zipEntry = new ZipEntry(key + ".xlsx");
zipOutputStream.putNextEntry(zipEntry);
// 先写入到临时流
EasyExcel.write(excelOutputStream, I18nExcel.class).sheet(key).doWrite(value);
zipOutputStream.write(excelOutputStream.toByteArray());
zipOutputStream.closeEntry();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
/**
* 将国际化资源列表写入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);
}
}

View File

@ -3,7 +3,7 @@ package cn.bunny.services.service.configuration.impl;
import cn.bunny.services.domain.system.configuration.dto.WebConfigurationDto;
import cn.bunny.services.domain.system.configuration.entity.WebConfiguration;
import cn.bunny.services.domain.common.constant.RedisUserConstant;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.exception.AuthCustomerException;
import cn.bunny.services.service.configuration.ConfigurationService;
import com.alibaba.fastjson2.JSON;

View File

@ -7,7 +7,7 @@ import cn.bunny.services.domain.system.email.entity.EmailTemplate;
import cn.bunny.services.domain.system.email.vo.EmailTemplateVo;
import cn.bunny.services.domain.common.enums.EmailTemplateEnums;
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.common.enums.ResultCodeEnum;
import cn.bunny.services.exception.AuthCustomerException;
import cn.bunny.services.mapper.configuration.EmailTemplateMapper;
import cn.bunny.services.service.configuration.EmailTemplateService;

View File

@ -6,7 +6,7 @@ import cn.bunny.services.domain.system.email.dto.EmailUsersUpdateDto;
import cn.bunny.services.domain.system.email.entity.EmailUsers;
import cn.bunny.services.domain.system.email.vo.EmailUsersVo;
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.common.enums.ResultCodeEnum;
import cn.bunny.services.exception.AuthCustomerException;
import cn.bunny.services.mapper.configuration.EmailUsersMapper;
import cn.bunny.services.service.configuration.EmailUsersService;

View File

@ -1,10 +1,13 @@
package cn.bunny.services.service.configuration.impl;
import cn.bunny.services.core.event.listener.excel.I18nExcelListener;
import cn.bunny.services.core.strategy.export.ExcelExportStrategy;
import cn.bunny.services.core.strategy.export.JsonExportStrategy;
import cn.bunny.services.domain.common.constant.FileType;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
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;
import cn.bunny.services.domain.system.i18n.dto.I18nDto;
import cn.bunny.services.domain.system.i18n.dto.I18nUpdateByFileDto;
@ -12,12 +15,10 @@ import cn.bunny.services.domain.system.i18n.dto.I18nUpdateDto;
import cn.bunny.services.domain.system.i18n.entity.I18n;
import cn.bunny.services.domain.system.i18n.entity.I18nType;
import cn.bunny.services.domain.system.i18n.vo.I18nVo;
import cn.bunny.services.excel.I18nExcelListener;
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 com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson2.JSON;
@ -29,6 +30,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
@ -47,6 +49,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.zip.ZipOutputStream;
/**
@ -60,6 +63,7 @@ import java.util.zip.ZipOutputStream;
@Service
@Transactional
public class I18nServiceImpl extends ServiceImpl<I18nMapper, I18n> implements I18nService {
private static final String CACHE_NAMES = "i18n";
@Resource
private I18nTypeMapper i18nTypeMapper;
@ -70,13 +74,13 @@ public class I18nServiceImpl extends ServiceImpl<I18nMapper, I18n> implements I1
* @return 多语言返回内容
*/
@Override
@Cacheable(cacheNames = "i18n", key = "'i18n'", cacheManager = "cacheManagerWithMouth")
@Cacheable(cacheNames = CACHE_NAMES, key = "'i18nMap'", cacheManager = "cacheManagerWithMouth")
public HashMap<String, Object> getI18nMap() {
// 查找默认语言内容
I18nType i18nType = i18nTypeMapper.selectOne(Wrappers.<I18nType>lambdaQuery().eq(I18nType::getIsDefault, true));
List<I18n> i18nList = list();
HashMap<String, Object> hashMap = I18nHelper.getMapByI18nList(i18nList);
HashMap<String, Object> hashMap = getMapByI18nList(i18nList);
hashMap.put("local", Objects.requireNonNull(i18nType.getTypeName(), "zh"));
return hashMap;
@ -105,7 +109,7 @@ public class I18nServiceImpl extends ServiceImpl<I18nMapper, I18n> implements I1
* @param dto 添加表单
*/
@Override
@CacheEvict(cacheNames = "i18n", key = "'i18n'", beforeInvocation = true)
@CacheEvict(cacheNames = CACHE_NAMES, key = "'i18nMap'", beforeInvocation = true)
public void addI18n(@Valid I18nAddDto dto) {
String keyName = dto.getKeyName();
String typeName = dto.getTypeName();
@ -126,7 +130,7 @@ public class I18nServiceImpl extends ServiceImpl<I18nMapper, I18n> implements I1
* @param dto 更新表单
*/
@Override
@CacheEvict(cacheNames = "i18n", key = "'i18n'", beforeInvocation = true)
@CacheEvict(cacheNames = CACHE_NAMES, key = "'i18nMap'", beforeInvocation = true)
public void updateI18n(@Valid I18nUpdateDto dto) {
Long id = dto.getId();
@ -146,7 +150,7 @@ public class I18nServiceImpl extends ServiceImpl<I18nMapper, I18n> implements I1
* @param ids 删除id列表
*/
@Override
@CacheEvict(cacheNames = "i18n", key = "'i18n'", beforeInvocation = true)
@CacheEvict(cacheNames = CACHE_NAMES, key = "'i18nMap'", beforeInvocation = true)
public void deleteI18n(List<Long> ids) {
// 判断数据请求是否为空
if (ids.isEmpty()) throw new AuthCustomerException(ResultCodeEnum.REQUEST_IS_EMPTY);
@ -163,18 +167,35 @@ public class I18nServiceImpl extends ServiceImpl<I18nMapper, I18n> implements I1
@Override
public ResponseEntity<byte[]> downloadI18n(String type) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try (ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) {
try (ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) {
// 查找默认语言内容
List<I18n> i18nList = list();
// 类型是Excel写入Excel
if (type.equals(FileType.EXCEL)) {
I18nHelper.writeExcel(i18nList, zipOutputStream);
i18nList.stream()
.collect(Collectors.groupingBy(
I18n::getTypeName,
Collectors.mapping((I18n i18n) -> {
String keyName = i18n.getKeyName();
String translation = i18n.getTranslation();
return I18nExcel.builder().keyName(keyName).translation(translation).build();
}, Collectors.toList())
))
.forEach((key, value) -> {
ExcelExportStrategy excelExportStrategy = new ExcelExportStrategy(I18nExcel.class, key);
excelExportStrategy.export(value, zipOutputStream, key + ".xlsx");
});
}
// 其他格式写入JSON
else {
I18nHelper.writeJson(i18nList, zipOutputStream);
HashMap<String, Object> hashMap = getMapByI18nList(i18nList);
hashMap.forEach((k, v) -> {
JsonExportStrategy jsonExportStrategy = new JsonExportStrategy();
jsonExportStrategy.export(v, zipOutputStream, k + ".json");
});
}
// 设置响应头
@ -193,7 +214,7 @@ public class I18nServiceImpl extends ServiceImpl<I18nMapper, I18n> implements I1
* @param dto 文件更新对象
*/
@Override
@CacheEvict(cacheNames = "i18n", key = "'i18n'", beforeInvocation = true)
@CacheEvict(cacheNames = CACHE_NAMES, key = "'i18nMap'", beforeInvocation = true)
public void uploadI18nFile(I18nUpdateByFileDto dto) {
String type = dto.getType();
MultipartFile file = dto.getFile();
@ -240,4 +261,32 @@ public class I18nServiceImpl extends ServiceImpl<I18nMapper, I18n> implements I1
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 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);
}
}

View File

@ -1,17 +1,20 @@
package cn.bunny.services.service.configuration.impl;
import cn.bunny.services.domain.common.enums.ResultCodeEnum;
import cn.bunny.services.domain.system.i18n.dto.I18nTypeAddDto;
import cn.bunny.services.domain.system.i18n.dto.I18nTypeDto;
import cn.bunny.services.domain.system.i18n.dto.I18nTypeUpdateDto;
import cn.bunny.services.domain.system.i18n.entity.I18nType;
import cn.bunny.services.domain.system.i18n.vo.I18nTypeVo;
import cn.bunny.services.domain.common.model.vo.result.ResultCodeEnum;
import cn.bunny.services.exception.AuthCustomerException;
import cn.bunny.services.mapper.configuration.I18nTypeMapper;
import cn.bunny.services.service.configuration.I18nTypeService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -28,6 +31,7 @@ import java.util.List;
@Service
@Transactional
public class I18nTypeServiceImpl extends ServiceImpl<I18nTypeMapper, I18nType> implements I18nTypeService {
private static final String CACHE_NAMES = "i18n";
/**
* 获取多语言类型
@ -35,6 +39,7 @@ public class I18nTypeServiceImpl extends ServiceImpl<I18nTypeMapper, I18nType> i
* @return 多语言类型列表
*/
@Override
@Cacheable(cacheNames = CACHE_NAMES, key = "'i18nTypeList'", cacheManager = "cacheManagerWithMouth")
public List<I18nTypeVo> getI18nTypeList(I18nTypeDto dto) {
List<I18nType> i18nTypeList = baseMapper.selectListByPage(dto);
return i18nTypeList.stream().map(i18nType -> {
@ -50,6 +55,10 @@ public class I18nTypeServiceImpl extends ServiceImpl<I18nTypeMapper, I18nType> i
* @param dto 多语言类型添加
*/
@Override
@Caching(evict = {
@CacheEvict(cacheNames = CACHE_NAMES, key = "'i18nMap'", beforeInvocation = true),
@CacheEvict(cacheNames = CACHE_NAMES, key = "'i18nTypeList'", beforeInvocation = true),
})
public void addI18nType(I18nTypeAddDto dto) {
String typeName = dto.getTypeName();
Boolean isDefault = dto.getIsDefault();
@ -77,14 +86,18 @@ public class I18nTypeServiceImpl extends ServiceImpl<I18nTypeMapper, I18nType> i
* @param dto 多语言类型更新
*/
@Override
@Caching(evict = {
@CacheEvict(cacheNames = CACHE_NAMES, key = "'i18nMap'", beforeInvocation = true),
@CacheEvict(cacheNames = CACHE_NAMES, key = "'i18nTypeList'", beforeInvocation = true),
})
public void updateI18nType(I18nTypeUpdateDto dto) {
Long id = dto.getId();
Boolean isDefault = dto.getIsDefault();
I18nType i18nType = new I18nType();
// 查询更新的内容是否存在
List<I18nType> i18nTypeList = list(Wrappers.<I18nType>lambdaQuery().eq(I18nType::getId, id));
if (i18nTypeList.isEmpty()) throw new AuthCustomerException(ResultCodeEnum.DATA_NOT_EXIST);
I18nType dbI18nType = getOne(Wrappers.<I18nType>lambdaQuery().eq(I18nType::getId, id));
if (dbI18nType == null) throw new AuthCustomerException(ResultCodeEnum.DATA_NOT_EXIST);
// 如果是默认将其它内容设为false
if (isDefault) {
@ -104,6 +117,10 @@ public class I18nTypeServiceImpl extends ServiceImpl<I18nTypeMapper, I18nType> i
* @param ids 删除id列表
*/
@Override
@Caching(evict = {
@CacheEvict(cacheNames = CACHE_NAMES, key = "'i18nMap'", beforeInvocation = true),
@CacheEvict(cacheNames = CACHE_NAMES, key = "'i18nTypeList'", beforeInvocation = true),
})
public void deleteI18nType(List<Long> ids) {
// 判断数据请求是否为空
if (ids.isEmpty()) throw new AuthCustomerException(ResultCodeEnum.REQUEST_IS_EMPTY);

Some files were not shown because too many files have changed in this diff Show More