Compare commits
90 Commits
dev-cloud-
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee41e544f5 | ||
|
|
08accdfdd4 | ||
|
|
406f20f7cb | ||
|
|
fb130a65f3 | ||
|
|
8847f3edef | ||
|
|
18bb9f9c95 | ||
|
|
979f2aa5f0 | ||
|
|
189930f0ab | ||
|
|
3c78e2bb53 | ||
|
|
63dc394c54 | ||
|
|
f24137eb8f | ||
|
|
504fc94b8c | ||
|
|
6ef6a344c6 | ||
|
|
5e3e0890f3 | ||
|
|
b7a7fca5f3 | ||
|
|
6adfe238be | ||
|
|
c1eac2a224 | ||
|
|
c5b01251d8 | ||
|
|
d40232d335 | ||
|
|
f37ea57bce | ||
|
|
b366869a00 | ||
|
|
a8e2887bbf | ||
|
|
92b772373b | ||
|
|
8f433a2594 | ||
|
|
e0db7a251c | ||
|
|
84bb7410cf | ||
|
|
2d75478bb7 | ||
|
|
5e47425ab5 | ||
|
|
a4d6306259 | ||
|
|
852eb05cbf | ||
|
|
90f98d66cc | ||
|
|
bf6e0dd952 | ||
|
|
04da9f9c02 | ||
|
|
b4525cd1a6 | ||
|
|
b51f113053 | ||
|
|
685cc3d92c | ||
|
|
e512fc29e0 | ||
|
|
5d19f5589a | ||
|
|
d6be017f37 | ||
|
|
db5bda1bba | ||
|
|
c9929c84d2 | ||
|
|
f91ecee6cc | ||
|
|
142c24ca90 | ||
|
|
a59dd25156 | ||
|
|
d23500d656 | ||
|
|
fe8d987527 | ||
|
|
a3ec97fec1 | ||
|
|
ea37855991 | ||
|
|
c54a0db6ab | ||
|
|
f0633dfcea | ||
|
|
ccfdd18516 | ||
|
|
f4ce15986c | ||
|
|
46da855dd4 | ||
|
|
b06d2503a4 | ||
|
|
b2c882f70f | ||
|
|
c8899a20a4 | ||
|
|
59e5777e28 | ||
|
|
c724387a16 | ||
|
|
ced025e292 | ||
|
|
37e264491f | ||
|
|
d379f79869 | ||
|
|
be4adbe00b | ||
| 33fc749363 | |||
|
|
eb5978eb9d | ||
|
|
db7dbcc97e | ||
|
|
cba659010a | ||
| 0a8c05e1df | |||
|
|
7a961d63a6 | ||
|
|
16779413a6 | ||
|
|
c0c2e492b8 | ||
|
|
023d35fecd | ||
| 895ae7383a | |||
|
|
0d12345ed4 | ||
| e6c1041202 | |||
| d7b36d9f59 | |||
| fb81670776 | |||
| f22189bc00 | |||
|
|
74d030a97e | ||
|
|
609bef8a87 | ||
| 0fa91a472d | |||
|
|
bdd3f251a2 | ||
| 4a0b1499c7 | |||
| 74064a4dc4 | |||
| 4c4f1acf32 | |||
|
|
92246a6767 | ||
|
|
6419f88c63 | ||
|
|
eee546fcc4 | ||
| 714b759050 | |||
|
|
2d3024b3e7 | ||
|
|
8b79ae515d |
@@ -1,72 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>agileboot</artifactId>
|
||||
<groupId>com.agileboot</groupId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>agileboot-admin</artifactId>
|
||||
|
||||
<description>
|
||||
web服务入口
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- 业务领域 -->
|
||||
<dependency>
|
||||
<groupId>com.agileboot</groupId>
|
||||
<artifactId>agileboot-domain</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.junit.vintage</groupId>
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${maven.surefire.plugin.version}</version>
|
||||
<!-- 想跑test的话 设置成false -->
|
||||
<configuration>
|
||||
<skipTests>false</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.agileboot.admin;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
/**
|
||||
* 启动程序
|
||||
* 定制banner.txt的网站
|
||||
* http://patorjk.com/software/taag
|
||||
* http://www.network-science.de/ascii/
|
||||
* http://www.degraeve.com/img2txt.php
|
||||
* http://life.chacuo.net/convertfont2char
|
||||
* @author valarchie
|
||||
*/
|
||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
|
||||
@ComponentScan(basePackages = "com.agileboot.*")
|
||||
public class AgileBootAdminApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AgileBootAdminApplication.class, args);
|
||||
String successMsg = " ____ _ _ __ _ _ \n"
|
||||
+ " / ___| | |_ __ _ _ __ | |_ _ _ _ __ ___ _ _ ___ ___ ___ ___ ___ / _| _ _ | || |\n"
|
||||
+ " \\___ \\ | __|/ _` || '__|| __| | | | || '_ \\ / __|| | | | / __|/ __|/ _ \\/ __|/ __|| |_ | | | || || |\n"
|
||||
+ " ___) || |_| (_| || | | |_ | |_| || |_) | \\__ \\| |_| || (__| (__| __/\\__ \\\\__ \\| _|| |_| || ||_|\n"
|
||||
+ " |____/ \\__|\\__,_||_| \\__| \\__,_|| .__/ |___/ \\__,_| \\___|\\___|\\___||___/|___/|_| \\__,_||_|(_)\n"
|
||||
+ " |_| ";
|
||||
|
||||
System.out.println(successMsg);
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
package com.agileboot.admin.controller.common;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
import com.agileboot.common.constant.Constants.UploadSubDir;
|
||||
import com.agileboot.common.core.dto.ResponseDTO;
|
||||
import com.agileboot.common.exception.ApiException;
|
||||
import com.agileboot.common.exception.error.ErrorCode;
|
||||
import com.agileboot.common.exception.error.ErrorCode.Business;
|
||||
import com.agileboot.common.utils.ServletHolderUtil;
|
||||
import com.agileboot.common.utils.file.FileUploadUtils;
|
||||
import com.agileboot.common.utils.jackson.JacksonUtil;
|
||||
import com.agileboot.domain.common.dto.UploadDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 通用请求处理
|
||||
* TODO 需要重构
|
||||
* @author valarchie
|
||||
*/
|
||||
@Tag(name = "上传API", description = "上传相关接口")
|
||||
@RestController
|
||||
@RequestMapping("/file")
|
||||
@Slf4j
|
||||
public class FileController {
|
||||
|
||||
|
||||
/**
|
||||
* 通用下载请求
|
||||
* download接口 其实不是很有必要
|
||||
* @param fileName 文件名称
|
||||
*/
|
||||
@Operation(summary = "下载文件")
|
||||
@GetMapping("/download")
|
||||
public ResponseEntity<byte[]> fileDownload(String fileName, HttpServletResponse response) {
|
||||
try {
|
||||
if (!FileUploadUtils.isAllowDownload(fileName)) {
|
||||
// 返回类型是ResponseEntity 不能捕获异常, 需要手动将错误填到 ResponseEntity
|
||||
ResponseDTO<Object> fail = ResponseDTO.fail(
|
||||
new ApiException(Business.COMMON_FILE_NOT_ALLOWED_TO_DOWNLOAD, fileName));
|
||||
return new ResponseEntity<>(JacksonUtil.to(fail).getBytes(), null, HttpStatus.OK);
|
||||
}
|
||||
|
||||
String filePath = FileUploadUtils.getFileAbsolutePath(UploadSubDir.DOWNLOAD_PATH, fileName);
|
||||
|
||||
HttpHeaders downloadHeader = FileUploadUtils.getDownloadHeader(fileName);
|
||||
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
return new ResponseEntity<>(FileUtil.readBytes(filePath), downloadHeader, HttpStatus.OK);
|
||||
} catch (Exception e) {
|
||||
log.error("下载文件失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用上传请求(单个)
|
||||
*/
|
||||
@Operation(summary = "单个上传文件")
|
||||
@PostMapping("/upload")
|
||||
public ResponseDTO<UploadDTO> uploadFile(MultipartFile file) {
|
||||
if (file == null) {
|
||||
throw new ApiException(ErrorCode.Business.UPLOAD_FILE_IS_EMPTY);
|
||||
}
|
||||
|
||||
// 上传并返回新文件名称
|
||||
String fileName = FileUploadUtils.upload(UploadSubDir.UPLOAD_PATH, file);
|
||||
|
||||
String url = ServletHolderUtil.getContextUrl() + fileName;
|
||||
|
||||
UploadDTO uploadDTO = UploadDTO.builder()
|
||||
// 全路径
|
||||
.url(url)
|
||||
// 相对路径
|
||||
.fileName(fileName)
|
||||
// 新生成的文件名
|
||||
.newFileName(FileNameUtil.getName(fileName))
|
||||
// 原始的文件名
|
||||
.originalFilename(file.getOriginalFilename()).build();
|
||||
|
||||
return ResponseDTO.ok(uploadDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用上传请求(多个)
|
||||
*/
|
||||
@Operation(summary = "多个上传文件")
|
||||
@PostMapping("/uploads")
|
||||
public ResponseDTO<List<UploadDTO>> uploadFiles(List<MultipartFile> files) {
|
||||
if (CollUtil.isEmpty(files)) {
|
||||
throw new ApiException(ErrorCode.Business.UPLOAD_FILE_IS_EMPTY);
|
||||
}
|
||||
|
||||
List<UploadDTO> uploads = new ArrayList<>();
|
||||
|
||||
for (MultipartFile file : files) {
|
||||
if (file != null) {
|
||||
// 上传并返回新文件名称
|
||||
String fileName = FileUploadUtils.upload(UploadSubDir.UPLOAD_PATH, file);
|
||||
String url = ServletHolderUtil.getContextUrl() + fileName;
|
||||
UploadDTO uploadDTO = UploadDTO.builder()
|
||||
.url(url)
|
||||
.fileName(fileName)
|
||||
.newFileName(FileNameUtil.getName(fileName))
|
||||
.originalFilename(file.getOriginalFilename()).build();
|
||||
|
||||
uploads.add(uploadDTO);
|
||||
|
||||
}
|
||||
}
|
||||
return ResponseDTO.ok(uploads);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
package com.agileboot.admin.controller.common;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.agileboot.common.config.AgileBootConfig;
|
||||
import com.agileboot.common.core.dto.ResponseDTO;
|
||||
import com.agileboot.common.exception.ApiException;
|
||||
import com.agileboot.common.exception.error.ErrorCode.Business;
|
||||
import com.agileboot.domain.common.dto.CurrentLoginUserDTO;
|
||||
import com.agileboot.domain.common.dto.TokenDTO;
|
||||
import com.agileboot.domain.system.menu.MenuApplicationService;
|
||||
import com.agileboot.domain.system.menu.dto.RouterDTO;
|
||||
import com.agileboot.domain.system.user.UserApplicationService;
|
||||
import com.agileboot.domain.system.user.command.AddUserCommand;
|
||||
import com.agileboot.infrastructure.annotations.ratelimit.RateLimit;
|
||||
import com.agileboot.infrastructure.annotations.ratelimit.RateLimit.CacheType;
|
||||
import com.agileboot.infrastructure.annotations.ratelimit.RateLimit.LimitType;
|
||||
import com.agileboot.infrastructure.user.AuthenticationUtils;
|
||||
import com.agileboot.admin.customize.service.login.dto.CaptchaDTO;
|
||||
import com.agileboot.admin.customize.service.login.dto.ConfigDTO;
|
||||
import com.agileboot.admin.customize.service.login.command.LoginCommand;
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.infrastructure.annotations.ratelimit.RateLimitKey;
|
||||
import com.agileboot.admin.customize.service.login.LoginService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 首页
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Tag(name = "登录API", description = "登录相关接口")
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class LoginController {
|
||||
|
||||
private final LoginService loginService;
|
||||
|
||||
private final MenuApplicationService menuApplicationService;
|
||||
|
||||
private final UserApplicationService userApplicationService;
|
||||
|
||||
private final AgileBootConfig agileBootConfig;
|
||||
|
||||
/**
|
||||
* 访问首页,提示语
|
||||
*/
|
||||
@Operation(summary = "首页")
|
||||
@GetMapping("/")
|
||||
@RateLimit(key = RateLimitKey.TEST_KEY, time = 10, maxCount = 5, cacheType = CacheType.Map,
|
||||
limitType = LimitType.GLOBAL)
|
||||
public String index() {
|
||||
return StrUtil.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。",
|
||||
agileBootConfig.getName(), agileBootConfig.getVersion());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取系统的内置配置
|
||||
*
|
||||
* @return 配置信息
|
||||
*/
|
||||
@GetMapping("/getConfig")
|
||||
public ResponseDTO<ConfigDTO> getConfig() {
|
||||
ConfigDTO configDTO = loginService.getConfig();
|
||||
return ResponseDTO.ok(configDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成验证码
|
||||
*/
|
||||
@Operation(summary = "验证码")
|
||||
@RateLimit(key = RateLimitKey.LOGIN_CAPTCHA_KEY, time = 10, maxCount = 10, cacheType = CacheType.REDIS,
|
||||
limitType = LimitType.IP)
|
||||
@GetMapping("/captchaImage")
|
||||
public ResponseDTO<CaptchaDTO> getCaptchaImg() {
|
||||
CaptchaDTO captchaImg = loginService.generateCaptchaImg();
|
||||
return ResponseDTO.ok(captchaImg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录方法
|
||||
*
|
||||
* @param loginCommand 登录信息
|
||||
* @return 结果
|
||||
*/
|
||||
@Operation(summary = "登录")
|
||||
@PostMapping("/login")
|
||||
public ResponseDTO<TokenDTO> login(@RequestBody LoginCommand loginCommand) {
|
||||
// 生成令牌
|
||||
String token = loginService.login(loginCommand);
|
||||
SystemLoginUser loginUser = AuthenticationUtils.getSystemLoginUser();
|
||||
CurrentLoginUserDTO currentUserDTO = userApplicationService.getLoginUserInfo(loginUser);
|
||||
|
||||
return ResponseDTO.ok(new TokenDTO(token, currentUserDTO));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*
|
||||
* @return 用户信息
|
||||
*/
|
||||
@Operation(summary = "获取当前登录用户信息")
|
||||
@GetMapping("/getLoginUserInfo")
|
||||
public ResponseDTO<CurrentLoginUserDTO> getLoginUserInfo() {
|
||||
SystemLoginUser loginUser = AuthenticationUtils.getSystemLoginUser();
|
||||
|
||||
CurrentLoginUserDTO currentUserDTO = userApplicationService.getLoginUserInfo(loginUser);
|
||||
|
||||
return ResponseDTO.ok(currentUserDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取路由信息
|
||||
* TODO 如果要在前端开启路由缓存的话 需要在ServerConfig.json 中 设置CachingAsyncRoutes=true 避免一直重复请求路由接口
|
||||
* @return 路由信息
|
||||
*/
|
||||
@Operation(summary = "获取用户对应的菜单路由", description = "用于动态生成路由")
|
||||
@GetMapping("/getRouters")
|
||||
public ResponseDTO<List<RouterDTO>> getRouters() {
|
||||
SystemLoginUser loginUser = AuthenticationUtils.getSystemLoginUser();
|
||||
List<RouterDTO> routerTree = menuApplicationService.getRouterTree(loginUser);
|
||||
return ResponseDTO.ok(routerTree);
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "注册接口", description = "暂未实现")
|
||||
@PostMapping("/register")
|
||||
public ResponseDTO<Void> register(@RequestBody AddUserCommand command) {
|
||||
return ResponseDTO.fail(new ApiException(Business.COMMON_UNSUPPORTED_OPERATION));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package com.agileboot.admin.controller.system;
|
||||
|
||||
import com.agileboot.common.core.base.BaseController;
|
||||
import com.agileboot.common.core.dto.ResponseDTO;
|
||||
import com.agileboot.common.core.page.PageDTO;
|
||||
import com.agileboot.domain.common.cache.CacheCenter;
|
||||
import com.agileboot.domain.system.monitor.MonitorApplicationService;
|
||||
import com.agileboot.domain.system.monitor.dto.OnlineUserDTO;
|
||||
import com.agileboot.domain.system.monitor.dto.RedisCacheInfoDTO;
|
||||
import com.agileboot.domain.system.monitor.dto.ServerInfo;
|
||||
import com.agileboot.admin.customize.aop.accessLog.AccessLog;
|
||||
import com.agileboot.common.enums.common.BusinessTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 缓存监控
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Tag(name = "监控API", description = "监控相关信息")
|
||||
@RestController
|
||||
@RequestMapping("/monitor")
|
||||
@RequiredArgsConstructor
|
||||
public class MonitorController extends BaseController {
|
||||
|
||||
private final MonitorApplicationService monitorApplicationService;
|
||||
|
||||
@Operation(summary = "Redis信息")
|
||||
@PreAuthorize("@permission.has('monitor:cache:list')")
|
||||
@GetMapping("/cacheInfo")
|
||||
public ResponseDTO<RedisCacheInfoDTO> getRedisCacheInfo() {
|
||||
RedisCacheInfoDTO redisCacheInfo = monitorApplicationService.getRedisCacheInfo();
|
||||
return ResponseDTO.ok(redisCacheInfo);
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "服务器信息")
|
||||
@PreAuthorize("@permission.has('monitor:server:list')")
|
||||
@GetMapping("/serverInfo")
|
||||
public ResponseDTO<ServerInfo> getServerInfo() {
|
||||
ServerInfo serverInfo = monitorApplicationService.getServerInfo();
|
||||
return ResponseDTO.ok(serverInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取在线用户列表
|
||||
*
|
||||
* @param ipAddress ip地址
|
||||
* @param username 用户名
|
||||
* @return 分页处理后的在线用户信息
|
||||
*/
|
||||
@Operation(summary = "在线用户列表")
|
||||
@PreAuthorize("@permission.has('monitor:online:list')")
|
||||
@GetMapping("/onlineUsers")
|
||||
public ResponseDTO<PageDTO<OnlineUserDTO>> onlineUsers(String ipAddress, String username) {
|
||||
List<OnlineUserDTO> onlineUserList = monitorApplicationService.getOnlineUserList(username, ipAddress);
|
||||
return ResponseDTO.ok(new PageDTO<>(onlineUserList));
|
||||
}
|
||||
|
||||
/**
|
||||
* 强退用户
|
||||
*/
|
||||
@Operation(summary = "强退用户")
|
||||
@PreAuthorize("@permission.has('monitor:online:forceLogout')")
|
||||
@AccessLog(title = "在线用户", businessType = BusinessTypeEnum.FORCE_LOGOUT)
|
||||
@DeleteMapping("/onlineUser/{tokenId}")
|
||||
public ResponseDTO<Void> logoutOnlineUser(@PathVariable String tokenId) {
|
||||
CacheCenter.loginUserCache.delete(tokenId);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package com.agileboot.admin.controller.system;
|
||||
|
||||
import com.agileboot.common.core.base.BaseController;
|
||||
import com.agileboot.common.core.dto.ResponseDTO;
|
||||
import com.agileboot.common.core.page.PageDTO;
|
||||
import com.agileboot.domain.common.cache.CacheCenter;
|
||||
import com.agileboot.domain.system.config.ConfigApplicationService;
|
||||
import com.agileboot.domain.system.config.command.ConfigUpdateCommand;
|
||||
import com.agileboot.domain.system.config.dto.ConfigDTO;
|
||||
import com.agileboot.domain.system.config.query.ConfigQuery;
|
||||
import com.agileboot.admin.customize.aop.accessLog.AccessLog;
|
||||
import com.agileboot.common.enums.common.BusinessTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 参数配置 信息操作处理
|
||||
* @author valarchie
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/system")
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "配置API", description = "配置相关的增删查改")
|
||||
public class SysConfigController extends BaseController {
|
||||
|
||||
private final ConfigApplicationService configApplicationService;
|
||||
|
||||
/**
|
||||
* 获取参数配置列表
|
||||
*/
|
||||
@Operation(summary = "参数列表", description = "分页获取配置参数列表")
|
||||
@PreAuthorize("@permission.has('system:config:list')")
|
||||
@GetMapping("/configs")
|
||||
public ResponseDTO<PageDTO<ConfigDTO>> list(ConfigQuery query) {
|
||||
PageDTO<ConfigDTO> page = configApplicationService.getConfigList(query);
|
||||
return ResponseDTO.ok(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据参数编号获取详细信息
|
||||
*/
|
||||
@PreAuthorize("@permission.has('system:config:query')")
|
||||
@GetMapping(value = "/config/{configId}")
|
||||
@Operation(summary = "配置信息", description = "配置的详细信息")
|
||||
public ResponseDTO<ConfigDTO> getInfo(@NotNull @Positive @PathVariable Long configId) {
|
||||
ConfigDTO config = configApplicationService.getConfigInfo(configId);
|
||||
return ResponseDTO.ok(config);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 修改参数配置
|
||||
*/
|
||||
@PreAuthorize("@permission.has('system:config:edit')")
|
||||
@AccessLog(title = "参数管理", businessType = BusinessTypeEnum.MODIFY)
|
||||
@Operation(summary = "配置修改", description = "配置修改")
|
||||
@PutMapping(value = "/config/{configId}")
|
||||
public ResponseDTO<Void> edit(@NotNull @Positive @PathVariable Long configId, @RequestBody ConfigUpdateCommand config) {
|
||||
config.setConfigId(configId);
|
||||
configApplicationService.updateConfig(config);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新参数缓存
|
||||
*/
|
||||
@Operation(summary = "刷新配置缓存")
|
||||
@PreAuthorize("@permission.has('system:config:remove')")
|
||||
@AccessLog(title = "参数管理", businessType = BusinessTypeEnum.CLEAN)
|
||||
@DeleteMapping("/configs/cache")
|
||||
public ResponseDTO<Void> refreshCache() {
|
||||
CacheCenter.configCache.invalidateAll();
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
package com.agileboot.admin.controller.system;
|
||||
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import com.agileboot.common.core.base.BaseController;
|
||||
import com.agileboot.common.core.dto.ResponseDTO;
|
||||
import com.agileboot.domain.system.dept.DeptApplicationService;
|
||||
import com.agileboot.domain.system.dept.command.AddDeptCommand;
|
||||
import com.agileboot.domain.system.dept.command.UpdateDeptCommand;
|
||||
import com.agileboot.domain.system.dept.dto.DeptDTO;
|
||||
import com.agileboot.domain.system.dept.query.DeptQuery;
|
||||
import com.agileboot.admin.customize.aop.accessLog.AccessLog;
|
||||
import com.agileboot.common.enums.common.BusinessTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.util.List;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 部门信息
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/system")
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "部门API", description = "部门相关的增删查改")
|
||||
public class SysDeptController extends BaseController {
|
||||
|
||||
private final DeptApplicationService deptApplicationService;
|
||||
|
||||
/**
|
||||
* 获取部门列表
|
||||
*/
|
||||
@Operation(summary = "部门列表")
|
||||
@PreAuthorize("@permission.has('system:dept:list')")
|
||||
@GetMapping("/depts")
|
||||
public ResponseDTO<List<DeptDTO>> list(DeptQuery query) {
|
||||
List<DeptDTO> deptList = deptApplicationService.getDeptList(query);
|
||||
return ResponseDTO.ok(deptList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部门编号获取详细信息
|
||||
*/
|
||||
@Operation(summary = "部门详情")
|
||||
@PreAuthorize("@permission.has('system:dept:query')")
|
||||
@GetMapping(value = "/dept/{deptId}")
|
||||
public ResponseDTO<DeptDTO> getInfo(@PathVariable Long deptId) {
|
||||
DeptDTO dept = deptApplicationService.getDeptInfo(deptId);
|
||||
return ResponseDTO.ok(dept);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门下拉树列表
|
||||
*/
|
||||
@Operation(summary = "获取部门树级结构")
|
||||
@GetMapping("/depts/dropdown")
|
||||
public ResponseDTO<List<Tree<Long>>> dropdownList() {
|
||||
List<Tree<Long>> deptTree = deptApplicationService.getDeptTree();
|
||||
return ResponseDTO.ok(deptTree);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增部门
|
||||
*/
|
||||
@Operation(summary = "新增部门")
|
||||
@PreAuthorize("@permission.has('system:dept:add')")
|
||||
@AccessLog(title = "部门管理", businessType = BusinessTypeEnum.ADD)
|
||||
@PostMapping("/dept")
|
||||
public ResponseDTO<Void> add(@RequestBody AddDeptCommand addCommand) {
|
||||
deptApplicationService.addDept(addCommand);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改部门
|
||||
*/
|
||||
@Operation(summary = "修改部门")
|
||||
@PreAuthorize("@permission.has('system:dept:edit') AND @dataScope.checkDeptId(#updateCommand.deptId)")
|
||||
@AccessLog(title = "部门管理", businessType = BusinessTypeEnum.MODIFY)
|
||||
@PutMapping("/dept/{deptId}")
|
||||
public ResponseDTO<Void> edit(@PathVariable("deptId")Long deptId, @RequestBody UpdateDeptCommand updateCommand) {
|
||||
updateCommand.setDeptId(deptId);
|
||||
deptApplicationService.updateDept(updateCommand);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除部门
|
||||
*/
|
||||
@Operation(summary = "删除部门")
|
||||
@PreAuthorize("@permission.has('system:dept:remove') AND @dataScope.checkDeptId(#deptId)")
|
||||
@AccessLog(title = "部门管理", businessType = BusinessTypeEnum.DELETE)
|
||||
@DeleteMapping("/dept/{deptId}")
|
||||
public ResponseDTO<Void> remove(@PathVariable @NotNull Long deptId) {
|
||||
deptApplicationService.removeDept(deptId);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
package com.agileboot.admin.controller.system;
|
||||
|
||||
|
||||
import com.agileboot.common.core.base.BaseController;
|
||||
import com.agileboot.common.core.dto.ResponseDTO;
|
||||
import com.agileboot.common.core.page.PageDTO;
|
||||
import com.agileboot.common.utils.poi.CustomExcelUtil;
|
||||
import com.agileboot.domain.common.command.BulkOperationCommand;
|
||||
import com.agileboot.domain.system.log.LogApplicationService;
|
||||
import com.agileboot.domain.system.log.dto.LoginLogDTO;
|
||||
import com.agileboot.domain.system.log.query.LoginLogQuery;
|
||||
import com.agileboot.domain.system.log.dto.OperationLogDTO;
|
||||
import com.agileboot.domain.system.log.query.OperationLogQuery;
|
||||
import com.agileboot.admin.customize.aop.accessLog.AccessLog;
|
||||
import com.agileboot.common.enums.common.BusinessTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 系统访问记录
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Tag(name = "日志API", description = "日志相关API")
|
||||
@RestController
|
||||
@RequestMapping("/logs")
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
public class SysLogsController extends BaseController {
|
||||
|
||||
private final LogApplicationService logApplicationService;
|
||||
|
||||
@Operation(summary = "登录日志列表")
|
||||
@PreAuthorize("@permission.has('monitor:logininfor:list')")
|
||||
@GetMapping("/loginLogs")
|
||||
public ResponseDTO<PageDTO<LoginLogDTO>> loginInfoList(LoginLogQuery query) {
|
||||
PageDTO<LoginLogDTO> pageDTO = logApplicationService.getLoginInfoList(query);
|
||||
return ResponseDTO.ok(pageDTO);
|
||||
}
|
||||
|
||||
@Operation(summary = "登录日志导出", description = "将登录日志导出到excel")
|
||||
@AccessLog(title = "登录日志", businessType = BusinessTypeEnum.EXPORT)
|
||||
@PreAuthorize("@permission.has('monitor:logininfor:export')")
|
||||
@GetMapping("/loginLogs/excel")
|
||||
public void loginInfosExcel(HttpServletResponse response, LoginLogQuery query) {
|
||||
PageDTO<LoginLogDTO> pageDTO = logApplicationService.getLoginInfoList(query);
|
||||
CustomExcelUtil.writeToResponse(pageDTO.getRows(), LoginLogDTO.class, response);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除登录日志")
|
||||
@PreAuthorize("@permission.has('monitor:logininfor:remove')")
|
||||
@AccessLog(title = "登录日志", businessType = BusinessTypeEnum.DELETE)
|
||||
@DeleteMapping("/loginLogs")
|
||||
public ResponseDTO<Void> removeLoginInfos(@RequestParam @NotNull @NotEmpty List<Long> ids) {
|
||||
logApplicationService.deleteLoginInfo(new BulkOperationCommand<>(ids));
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
@Operation(summary = "操作日志列表")
|
||||
@PreAuthorize("@permission.has('monitor:operlog:list')")
|
||||
@GetMapping("/operationLogs")
|
||||
public ResponseDTO<PageDTO<OperationLogDTO>> operationLogs(OperationLogQuery query) {
|
||||
PageDTO<OperationLogDTO> pageDTO = logApplicationService.getOperationLogList(query);
|
||||
return ResponseDTO.ok(pageDTO);
|
||||
}
|
||||
|
||||
// @GetMapping("/download")
|
||||
// public ResponseEntity<InputStreamResource> downloadFile() throws IOException {
|
||||
// // 从文件系统或其他位置获取文件输入流
|
||||
// File file = new File("path/to/file");
|
||||
// InputStream inputStream = new FileInputStream(file);
|
||||
// CustomExcelUtil.wri
|
||||
//
|
||||
// // 创建一个 InputStreamResource 对象,将文件输入流包装在其中
|
||||
// InputStreamResource resource = new InputStreamResource(inputStream);
|
||||
//
|
||||
// // 返回 ResponseEntity 对象,其中包含 InputStreamResource 对象和文件名
|
||||
// return ResponseEntity.ok()
|
||||
// .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + file.getName())
|
||||
// .contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
// .contentLength(file.length())
|
||||
// .body(resource);
|
||||
// }
|
||||
/**
|
||||
* 可否改成以上的形式 TODO
|
||||
* @param response
|
||||
* @param query
|
||||
*/
|
||||
@Operation(summary = "操作日志导出")
|
||||
@AccessLog(title = "操作日志", businessType = BusinessTypeEnum.EXPORT)
|
||||
@PreAuthorize("@permission.has('monitor:operlog:export')")
|
||||
@GetMapping("/operationLogs/excel")
|
||||
public void operationLogsExcel(HttpServletResponse response, OperationLogQuery query) {
|
||||
PageDTO<OperationLogDTO> pageDTO = logApplicationService.getOperationLogList(query);
|
||||
CustomExcelUtil.writeToResponse(pageDTO.getRows(), OperationLogDTO.class, response);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除操作日志")
|
||||
@AccessLog(title = "操作日志", businessType = BusinessTypeEnum.DELETE)
|
||||
@PreAuthorize("@permission.has('monitor:operlog:remove')")
|
||||
@DeleteMapping("/operationLogs")
|
||||
public ResponseDTO<Void> removeOperationLogs(@RequestParam List<Long> operationIds) {
|
||||
logApplicationService.deleteOperationLog(new BulkOperationCommand<>(operationIds));
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
package com.agileboot.admin.controller.system;
|
||||
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import com.agileboot.common.core.base.BaseController;
|
||||
import com.agileboot.common.core.dto.ResponseDTO;
|
||||
import com.agileboot.domain.system.menu.MenuApplicationService;
|
||||
import com.agileboot.domain.system.menu.command.AddMenuCommand;
|
||||
import com.agileboot.domain.system.menu.command.UpdateMenuCommand;
|
||||
import com.agileboot.domain.system.menu.dto.MenuDTO;
|
||||
import com.agileboot.domain.system.menu.dto.MenuDetailDTO;
|
||||
import com.agileboot.domain.system.menu.query.MenuQuery;
|
||||
import com.agileboot.admin.customize.aop.accessLog.AccessLog;
|
||||
import com.agileboot.infrastructure.user.AuthenticationUtils;
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.common.enums.common.BusinessTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.util.List;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.PositiveOrZero;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 菜单信息
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Tag(name = "菜单API", description = "菜单相关的增删查改")
|
||||
@RestController
|
||||
@RequestMapping("/system/menus")
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
public class SysMenuController extends BaseController {
|
||||
|
||||
private final MenuApplicationService menuApplicationService;
|
||||
|
||||
/**
|
||||
* 获取菜单列表
|
||||
*/
|
||||
@Operation(summary = "菜单列表")
|
||||
@PreAuthorize("@permission.has('system:menu:list')")
|
||||
@GetMapping
|
||||
public ResponseDTO<List<MenuDTO>> menuList(MenuQuery menuQuery) {
|
||||
List<MenuDTO> menuList = menuApplicationService.getMenuList(menuQuery);
|
||||
return ResponseDTO.ok(menuList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据菜单编号获取详细信息
|
||||
*/
|
||||
@Operation(summary = "菜单详情")
|
||||
@PreAuthorize("@permission.has('system:menu:query')")
|
||||
@GetMapping(value = "/{menuId}")
|
||||
public ResponseDTO<MenuDetailDTO> menuInfo(@PathVariable @NotNull @PositiveOrZero Long menuId) {
|
||||
MenuDetailDTO menu = menuApplicationService.getMenuInfo(menuId);
|
||||
return ResponseDTO.ok(menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取菜单下拉树列表
|
||||
*/
|
||||
@Operation(summary = "菜单列表(树级)", description = "菜单树级下拉框")
|
||||
@GetMapping("/dropdown")
|
||||
public ResponseDTO<List<Tree<Long>>> dropdownList() {
|
||||
SystemLoginUser loginUser = AuthenticationUtils.getSystemLoginUser();
|
||||
List<Tree<Long>> dropdownList = menuApplicationService.getDropdownList(loginUser);
|
||||
return ResponseDTO.ok(dropdownList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增菜单
|
||||
* 需支持一级菜单以及 多级菜单 子菜单为一个 或者 多个的情况
|
||||
* 隐藏菜单不显示 以及rank排序
|
||||
* 内链 和 外链
|
||||
*/
|
||||
@Operation(summary = "添加菜单")
|
||||
@PreAuthorize("@permission.has('system:menu:add')")
|
||||
@AccessLog(title = "菜单管理", businessType = BusinessTypeEnum.ADD)
|
||||
@PostMapping
|
||||
public ResponseDTO<Void> add(@RequestBody AddMenuCommand addCommand) {
|
||||
menuApplicationService.addMenu(addCommand);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改菜单
|
||||
*/
|
||||
@Operation(summary = "编辑菜单")
|
||||
@PreAuthorize("@permission.has('system:menu:edit')")
|
||||
@AccessLog(title = "菜单管理", businessType = BusinessTypeEnum.MODIFY)
|
||||
@PutMapping("/{menuId}")
|
||||
public ResponseDTO<Void> edit(@PathVariable("menuId") Long menuId, @RequestBody UpdateMenuCommand updateCommand) {
|
||||
updateCommand.setMenuId(menuId);
|
||||
menuApplicationService.updateMenu(updateCommand);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除菜单
|
||||
*/
|
||||
@Operation(summary = "删除菜单")
|
||||
@PreAuthorize("@permission.has('system:menu:remove')")
|
||||
@AccessLog(title = "菜单管理", businessType = BusinessTypeEnum.DELETE)
|
||||
@DeleteMapping("/{menuId}")
|
||||
public ResponseDTO<Void> remove(@PathVariable("menuId") Long menuId) {
|
||||
menuApplicationService.remove(menuId);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package com.agileboot.admin.controller.system;
|
||||
|
||||
import com.agileboot.common.core.base.BaseController;
|
||||
import com.agileboot.common.core.dto.ResponseDTO;
|
||||
import com.agileboot.common.core.page.PageDTO;
|
||||
import com.agileboot.domain.common.command.BulkOperationCommand;
|
||||
import com.agileboot.domain.system.notice.NoticeApplicationService;
|
||||
import com.agileboot.domain.system.notice.command.NoticeAddCommand;
|
||||
import com.agileboot.domain.system.notice.command.NoticeUpdateCommand;
|
||||
import com.agileboot.domain.system.notice.dto.NoticeDTO;
|
||||
import com.agileboot.domain.system.notice.query.NoticeQuery;
|
||||
import com.agileboot.admin.customize.aop.accessLog.AccessLog;
|
||||
import com.agileboot.infrastructure.annotations.unrepeatable.Unrepeatable;
|
||||
import com.agileboot.infrastructure.annotations.unrepeatable.Unrepeatable.CheckType;
|
||||
import com.agileboot.common.enums.common.BusinessTypeEnum;
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.util.List;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 公告 信息操作处理
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Tag(name = "公告API", description = "公告相关的增删查改")
|
||||
@RestController
|
||||
@RequestMapping("/system/notices")
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
public class SysNoticeController extends BaseController {
|
||||
|
||||
private final NoticeApplicationService noticeApplicationService;
|
||||
|
||||
/**
|
||||
* 获取通知公告列表
|
||||
*/
|
||||
@Operation(summary = "公告列表")
|
||||
@PreAuthorize("@permission.has('system:notice:list')")
|
||||
@GetMapping
|
||||
public ResponseDTO<PageDTO<NoticeDTO>> list(NoticeQuery query) {
|
||||
PageDTO<NoticeDTO> pageDTO = noticeApplicationService.getNoticeList(query);
|
||||
return ResponseDTO.ok(pageDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取通知公告列表
|
||||
* 从从库获取数据 例子 仅供参考
|
||||
*/
|
||||
@Operation(summary = "公告列表(从数据库从库获取)", description = "演示主从库的例子")
|
||||
@DS("slave")
|
||||
@PreAuthorize("@permission.has('system:notice:list')")
|
||||
@GetMapping("/database/slave")
|
||||
public ResponseDTO<PageDTO<NoticeDTO>> listFromSlave(NoticeQuery query) {
|
||||
PageDTO<NoticeDTO> pageDTO = noticeApplicationService.getNoticeList(query);
|
||||
return ResponseDTO.ok(pageDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据通知公告编号获取详细信息
|
||||
*/
|
||||
@Operation(summary = "公告详情")
|
||||
@PreAuthorize("@permission.has('system:notice:query')")
|
||||
@GetMapping(value = "/{noticeId}")
|
||||
public ResponseDTO<NoticeDTO> getInfo(@PathVariable @NotNull @Positive Long noticeId) {
|
||||
return ResponseDTO.ok(noticeApplicationService.getNoticeInfo(noticeId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增通知公告
|
||||
*/
|
||||
@Operation(summary = "添加公告")
|
||||
@Unrepeatable(interval = 60, checkType = CheckType.SYSTEM_USER)
|
||||
@PreAuthorize("@permission.has('system:notice:add')")
|
||||
@AccessLog(title = "通知公告", businessType = BusinessTypeEnum.ADD)
|
||||
@PostMapping
|
||||
public ResponseDTO<Void> add(@RequestBody NoticeAddCommand addCommand) {
|
||||
noticeApplicationService.addNotice(addCommand);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改通知公告
|
||||
*/
|
||||
@Operation(summary = "修改公告")
|
||||
@PreAuthorize("@permission.has('system:notice:edit')")
|
||||
@AccessLog(title = "通知公告", businessType = BusinessTypeEnum.MODIFY)
|
||||
@PutMapping("/{noticeId}")
|
||||
public ResponseDTO<Void> edit(@PathVariable Long noticeId, @RequestBody NoticeUpdateCommand updateCommand) {
|
||||
updateCommand.setNoticeId(noticeId);
|
||||
noticeApplicationService.updateNotice(updateCommand);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除通知公告
|
||||
*/
|
||||
@Operation(summary = "删除公告")
|
||||
@PreAuthorize("@permission.has('system:notice:remove')")
|
||||
@AccessLog(title = "通知公告", businessType = BusinessTypeEnum.DELETE)
|
||||
@DeleteMapping
|
||||
public ResponseDTO<Void> remove(@RequestParam List<Integer> noticeIds) {
|
||||
noticeApplicationService.deleteNotice(new BulkOperationCommand<>(noticeIds));
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package com.agileboot.admin.controller.system;
|
||||
|
||||
import com.agileboot.admin.customize.aop.accessLog.AccessLog;
|
||||
import com.agileboot.common.core.base.BaseController;
|
||||
import com.agileboot.common.core.dto.ResponseDTO;
|
||||
import com.agileboot.common.core.page.PageDTO;
|
||||
import com.agileboot.common.enums.common.BusinessTypeEnum;
|
||||
import com.agileboot.common.utils.poi.CustomExcelUtil;
|
||||
import com.agileboot.domain.common.command.BulkOperationCommand;
|
||||
import com.agileboot.domain.system.post.PostApplicationService;
|
||||
import com.agileboot.domain.system.post.command.AddPostCommand;
|
||||
import com.agileboot.domain.system.post.command.UpdatePostCommand;
|
||||
import com.agileboot.domain.system.post.dto.PostDTO;
|
||||
import com.agileboot.domain.system.post.query.PostQuery;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 岗位信息操作处理
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Tag(name = "职位API", description = "职位相关的增删查改")
|
||||
@RestController
|
||||
@RequestMapping("/system/post")
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
public class SysPostController extends BaseController {
|
||||
|
||||
private final PostApplicationService postApplicationService;
|
||||
|
||||
/**
|
||||
* 获取岗位列表
|
||||
*/
|
||||
@Operation(summary = "职位列表")
|
||||
@PreAuthorize("@permission.has('system:post:list')")
|
||||
@GetMapping("/list")
|
||||
public ResponseDTO<PageDTO<PostDTO>> list(PostQuery query) {
|
||||
PageDTO<PostDTO> pageDTO = postApplicationService.getPostList(query);
|
||||
return ResponseDTO.ok(pageDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出查询到的所有岗位信息到excel文件
|
||||
* @param response http响应
|
||||
* @param query 查询参数
|
||||
* @author Kevin Zhang
|
||||
* @date 2023-10-02
|
||||
*/
|
||||
@Operation(summary = "职位列表导出")
|
||||
@AccessLog(title = "岗位管理", businessType = BusinessTypeEnum.EXPORT)
|
||||
@PreAuthorize("@permission.has('system:post:export')")
|
||||
@GetMapping("/excel")
|
||||
public void export(HttpServletResponse response, PostQuery query) {
|
||||
List<PostDTO> all = postApplicationService.getPostListAll(query);
|
||||
CustomExcelUtil.writeToResponse(all, PostDTO.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据岗位编号获取详细信息
|
||||
*/
|
||||
@Operation(summary = "职位详情")
|
||||
@PreAuthorize("@permission.has('system:post:query')")
|
||||
@GetMapping(value = "/{postId}")
|
||||
public ResponseDTO<PostDTO> getInfo(@PathVariable Long postId) {
|
||||
PostDTO post = postApplicationService.getPostInfo(postId);
|
||||
return ResponseDTO.ok(post);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增岗位
|
||||
*/
|
||||
@Operation(summary = "添加职位")
|
||||
@PreAuthorize("@permission.has('system:post:add')")
|
||||
@AccessLog(title = "岗位管理", businessType = BusinessTypeEnum.ADD)
|
||||
@PostMapping
|
||||
public ResponseDTO<Void> add(@RequestBody AddPostCommand addCommand) {
|
||||
postApplicationService.addPost(addCommand);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改岗位
|
||||
*/
|
||||
@Operation(summary = "修改职位")
|
||||
@PreAuthorize("@permission.has('system:post:edit')")
|
||||
@AccessLog(title = "岗位管理", businessType = BusinessTypeEnum.MODIFY)
|
||||
@PutMapping
|
||||
public ResponseDTO<Void> edit(@RequestBody UpdatePostCommand updateCommand) {
|
||||
postApplicationService.updatePost(updateCommand);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除岗位
|
||||
*/
|
||||
@Operation(summary = "删除职位")
|
||||
@PreAuthorize("@permission.has('system:post:remove')")
|
||||
@AccessLog(title = "岗位管理", businessType = BusinessTypeEnum.DELETE)
|
||||
@DeleteMapping
|
||||
public ResponseDTO<Void> remove(@RequestParam @NotNull @NotEmpty List<Long> ids) {
|
||||
postApplicationService.deletePost(new BulkOperationCommand<>(ids));
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package com.agileboot.admin.controller.system;
|
||||
|
||||
import com.agileboot.common.constant.Constants.UploadSubDir;
|
||||
import com.agileboot.common.core.base.BaseController;
|
||||
import com.agileboot.common.core.dto.ResponseDTO;
|
||||
import com.agileboot.common.exception.ApiException;
|
||||
import com.agileboot.common.exception.error.ErrorCode;
|
||||
import com.agileboot.common.utils.file.FileUploadUtils;
|
||||
import com.agileboot.domain.common.dto.UploadFileDTO;
|
||||
import com.agileboot.domain.system.user.UserApplicationService;
|
||||
import com.agileboot.domain.system.user.command.UpdateProfileCommand;
|
||||
import com.agileboot.domain.system.user.command.UpdateUserAvatarCommand;
|
||||
import com.agileboot.domain.system.user.command.UpdateUserPasswordCommand;
|
||||
import com.agileboot.domain.system.user.dto.UserProfileDTO;
|
||||
import com.agileboot.admin.customize.aop.accessLog.AccessLog;
|
||||
import com.agileboot.infrastructure.user.AuthenticationUtils;
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.common.enums.common.BusinessTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 个人信息 业务处理
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Tag(name = "个人信息API", description = "个人信息相关接口")
|
||||
@RestController
|
||||
@RequestMapping("/system/user/profile")
|
||||
@RequiredArgsConstructor
|
||||
public class SysProfileController extends BaseController {
|
||||
|
||||
private final UserApplicationService userApplicationService;
|
||||
|
||||
/**
|
||||
* 个人信息
|
||||
*/
|
||||
@Operation(summary = "获取个人信息")
|
||||
@GetMapping
|
||||
public ResponseDTO<UserProfileDTO> profile() {
|
||||
SystemLoginUser user = AuthenticationUtils.getSystemLoginUser();
|
||||
UserProfileDTO userProfile = userApplicationService.getUserProfile(user.getUserId());
|
||||
return ResponseDTO.ok(userProfile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户
|
||||
*/
|
||||
@Operation(summary = "修改个人信息")
|
||||
@AccessLog(title = "个人信息", businessType = BusinessTypeEnum.MODIFY)
|
||||
@PutMapping
|
||||
public ResponseDTO<Void> updateProfile(@RequestBody UpdateProfileCommand command) {
|
||||
SystemLoginUser loginUser = AuthenticationUtils.getSystemLoginUser();
|
||||
command.setUserId(loginUser.getUserId());
|
||||
userApplicationService.updateUserProfile(command);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置密码
|
||||
*/
|
||||
@Operation(summary = "重置个人密码")
|
||||
@AccessLog(title = "个人信息", businessType = BusinessTypeEnum.MODIFY)
|
||||
@PutMapping("/password")
|
||||
public ResponseDTO<Void> updatePassword(@RequestBody UpdateUserPasswordCommand command) {
|
||||
SystemLoginUser loginUser = AuthenticationUtils.getSystemLoginUser();
|
||||
command.setUserId(loginUser.getUserId());
|
||||
userApplicationService.updatePasswordBySelf(loginUser, command);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 头像上传
|
||||
*/
|
||||
@Operation(summary = "修改个人头像")
|
||||
@AccessLog(title = "用户头像", businessType = BusinessTypeEnum.MODIFY)
|
||||
@PostMapping("/avatar")
|
||||
public ResponseDTO<UploadFileDTO> avatar(@RequestParam("avatarfile") MultipartFile file) {
|
||||
if (file.isEmpty()) {
|
||||
throw new ApiException(ErrorCode.Business.USER_UPLOAD_FILE_FAILED);
|
||||
}
|
||||
SystemLoginUser loginUser = AuthenticationUtils.getSystemLoginUser();
|
||||
String avatarUrl = FileUploadUtils.upload(UploadSubDir.AVATAR_PATH, file);
|
||||
|
||||
userApplicationService.updateUserAvatar(new UpdateUserAvatarCommand(loginUser.getUserId(), avatarUrl));
|
||||
return ResponseDTO.ok(new UploadFileDTO(avatarUrl));
|
||||
}
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
package com.agileboot.admin.controller.system;
|
||||
|
||||
import com.agileboot.common.core.base.BaseController;
|
||||
import com.agileboot.common.core.dto.ResponseDTO;
|
||||
import com.agileboot.common.core.page.PageDTO;
|
||||
import com.agileboot.common.utils.poi.CustomExcelUtil;
|
||||
import com.agileboot.domain.system.role.RoleApplicationService;
|
||||
import com.agileboot.domain.system.role.command.AddRoleCommand;
|
||||
import com.agileboot.domain.system.role.command.UpdateDataScopeCommand;
|
||||
import com.agileboot.domain.system.role.command.UpdateRoleCommand;
|
||||
import com.agileboot.domain.system.role.command.UpdateStatusCommand;
|
||||
import com.agileboot.domain.system.role.dto.RoleDTO;
|
||||
import com.agileboot.domain.system.role.query.AllocatedRoleQuery;
|
||||
import com.agileboot.domain.system.role.query.RoleQuery;
|
||||
import com.agileboot.domain.system.role.query.UnallocatedRoleQuery;
|
||||
import com.agileboot.domain.system.user.dto.UserDTO;
|
||||
import com.agileboot.admin.customize.aop.accessLog.AccessLog;
|
||||
import com.agileboot.common.enums.common.BusinessTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 角色信息
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Tag(name = "角色API", description = "角色相关的增删查改")
|
||||
@RestController
|
||||
@RequestMapping("/system/role")
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
public class SysRoleController extends BaseController {
|
||||
|
||||
private final RoleApplicationService roleApplicationService;
|
||||
|
||||
@Operation(summary = "角色列表")
|
||||
@PreAuthorize("@permission.has('system:role:list')")
|
||||
@GetMapping("/list")
|
||||
public ResponseDTO<PageDTO<RoleDTO>> list(RoleQuery query) {
|
||||
PageDTO<RoleDTO> pageDTO = roleApplicationService.getRoleList(query);
|
||||
return ResponseDTO.ok(pageDTO);
|
||||
}
|
||||
|
||||
@Operation(summary = "角色列表导出")
|
||||
@AccessLog(title = "角色管理", businessType = BusinessTypeEnum.EXPORT)
|
||||
@PreAuthorize("@permission.has('system:role:export')")
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, RoleQuery query) {
|
||||
PageDTO<RoleDTO> pageDTO = roleApplicationService.getRoleList(query);
|
||||
CustomExcelUtil.writeToResponse(pageDTO.getRows(), RoleDTO.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据角色编号获取详细信息
|
||||
*/
|
||||
@Operation(summary = "角色详情")
|
||||
@PreAuthorize("@permission.has('system:role:query')")
|
||||
@GetMapping(value = "/{roleId}")
|
||||
public ResponseDTO<RoleDTO> getInfo(@PathVariable @NotNull Long roleId) {
|
||||
RoleDTO roleInfo = roleApplicationService.getRoleInfo(roleId);
|
||||
return ResponseDTO.ok(roleInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增角色
|
||||
*/
|
||||
@Operation(summary = "添加角色")
|
||||
@PreAuthorize("@permission.has('system:role:add')")
|
||||
@AccessLog(title = "角色管理", businessType = BusinessTypeEnum.ADD)
|
||||
@PostMapping
|
||||
public ResponseDTO<Void> add(@RequestBody AddRoleCommand addCommand) {
|
||||
roleApplicationService.addRole(addCommand);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除角色
|
||||
*/
|
||||
@Operation(summary = "删除角色")
|
||||
@PreAuthorize("@permission.has('system:role:remove')")
|
||||
@AccessLog(title = "角色管理", businessType = BusinessTypeEnum.DELETE)
|
||||
@DeleteMapping(value = "/{roleId}")
|
||||
public ResponseDTO<Void> remove(@PathVariable("roleId") List<Long> roleIds) {
|
||||
roleApplicationService.deleteRoleByBulk(roleIds);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改保存角色
|
||||
*/
|
||||
@Operation(summary = "修改角色")
|
||||
@PreAuthorize("@permission.has('system:role:edit')")
|
||||
@AccessLog(title = "角色管理", businessType = BusinessTypeEnum.MODIFY)
|
||||
@PutMapping
|
||||
public ResponseDTO<Void> edit(@Validated @RequestBody UpdateRoleCommand updateCommand) {
|
||||
roleApplicationService.updateRole(updateCommand);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改保存数据权限
|
||||
*/
|
||||
@Operation(summary = "修改角色数据权限")
|
||||
@PreAuthorize("@permission.has('system:role:edit')")
|
||||
@AccessLog(title = "角色管理", businessType = BusinessTypeEnum.MODIFY)
|
||||
@PutMapping("/{roleId}/dataScope")
|
||||
public ResponseDTO<Void> dataScope(@PathVariable("roleId") Long roleId,
|
||||
@RequestBody UpdateDataScopeCommand command) {
|
||||
command.setRoleId(roleId);
|
||||
|
||||
roleApplicationService.updateDataScope(command);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色状态修改
|
||||
*/
|
||||
@Operation(summary = "修改角色状态")
|
||||
@PreAuthorize("@permission.has('system:role:edit')")
|
||||
@AccessLog(title = "角色管理", businessType = BusinessTypeEnum.MODIFY)
|
||||
@PutMapping("/{roleId}/status")
|
||||
public ResponseDTO<Void> changeStatus(@PathVariable("roleId") Long roleId,
|
||||
@RequestBody UpdateStatusCommand command) {
|
||||
command.setRoleId(roleId);
|
||||
|
||||
roleApplicationService.updateStatus(command);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询已分配用户角色列表
|
||||
*/
|
||||
@Operation(summary = "已关联该角色的用户列表")
|
||||
@PreAuthorize("@permission.has('system:role:list')")
|
||||
@GetMapping("/{roleId}/allocated/list")
|
||||
public ResponseDTO<PageDTO<UserDTO>> allocatedUserList(@PathVariable("roleId") Long roleId,
|
||||
AllocatedRoleQuery query) {
|
||||
query.setRoleId(roleId);
|
||||
PageDTO<UserDTO> page = roleApplicationService.getAllocatedUserList(query);
|
||||
return ResponseDTO.ok(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询未分配用户角色列表
|
||||
*/
|
||||
@Operation(summary = "未关联该角色的用户列表")
|
||||
@PreAuthorize("@permission.has('system:role:list')")
|
||||
@GetMapping("/{roleId}/unallocated/list")
|
||||
public ResponseDTO<PageDTO<UserDTO>> unallocatedUserList(@PathVariable("roleId") Long roleId,
|
||||
UnallocatedRoleQuery query) {
|
||||
query.setRoleId(roleId);
|
||||
PageDTO<UserDTO> page = roleApplicationService.getUnallocatedUserList(query);
|
||||
return ResponseDTO.ok(page);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 批量取消授权用户
|
||||
*/
|
||||
@Operation(summary = "批量解除角色和用户的关联")
|
||||
@PreAuthorize("@permission.has('system:role:edit')")
|
||||
@AccessLog(title = "角色管理", businessType = BusinessTypeEnum.GRANT)
|
||||
@DeleteMapping("/users/{userIds}/grant/bulk")
|
||||
public ResponseDTO<Void> deleteRoleOfUserByBulk(@PathVariable("userIds") List<Long> userIds) {
|
||||
roleApplicationService.deleteRoleOfUserByBulk(userIds);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量选择用户授权
|
||||
*/
|
||||
@Operation(summary = "批量添加用户和角色关联")
|
||||
@PreAuthorize("@permission.has('system:role:edit')")
|
||||
@AccessLog(title = "角色管理", businessType = BusinessTypeEnum.GRANT)
|
||||
@PostMapping("/{roleId}/users/{userIds}/grant/bulk")
|
||||
public ResponseDTO<Void> addRoleForUserByBulk(@PathVariable("roleId") Long roleId,
|
||||
@PathVariable("userIds") List<Long> userIds) {
|
||||
roleApplicationService.addRoleOfUserByBulk(roleId, userIds);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
package com.agileboot.admin.controller.system;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import com.agileboot.common.core.base.BaseController;
|
||||
import com.agileboot.common.core.dto.ResponseDTO;
|
||||
import com.agileboot.common.core.page.PageDTO;
|
||||
import com.agileboot.common.utils.poi.CustomExcelUtil;
|
||||
import com.agileboot.domain.common.command.BulkOperationCommand;
|
||||
import com.agileboot.domain.system.user.UserApplicationService;
|
||||
import com.agileboot.domain.system.user.command.AddUserCommand;
|
||||
import com.agileboot.domain.system.user.command.ChangeStatusCommand;
|
||||
import com.agileboot.domain.system.user.command.ResetPasswordCommand;
|
||||
import com.agileboot.domain.system.user.command.UpdateUserCommand;
|
||||
import com.agileboot.domain.system.user.dto.UserDTO;
|
||||
import com.agileboot.domain.system.user.dto.UserDetailDTO;
|
||||
import com.agileboot.domain.system.user.query.SearchUserQuery;
|
||||
import com.agileboot.admin.customize.aop.accessLog.AccessLog;
|
||||
import com.agileboot.infrastructure.user.AuthenticationUtils;
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.common.enums.common.BusinessTypeEnum;
|
||||
import com.agileboot.domain.system.user.db.SearchUserDO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
* @author valarchie
|
||||
*/
|
||||
@Tag(name = "用户API", description = "用户相关的增删查改")
|
||||
@RestController
|
||||
@RequestMapping("/system/users")
|
||||
@RequiredArgsConstructor
|
||||
public class SysUserController extends BaseController {
|
||||
|
||||
private final UserApplicationService userApplicationService;
|
||||
|
||||
/**
|
||||
* 获取用户列表
|
||||
*/
|
||||
@Operation(summary = "用户列表")
|
||||
@PreAuthorize("@permission.has('system:user:list') AND @dataScope.checkDeptId(#query.deptId)")
|
||||
@GetMapping
|
||||
public ResponseDTO<PageDTO<UserDTO>> userList(SearchUserQuery<SearchUserDO> query) {
|
||||
PageDTO<UserDTO> page = userApplicationService.getUserList(query);
|
||||
return ResponseDTO.ok(page);
|
||||
}
|
||||
|
||||
@Operation(summary = "用户列表导出")
|
||||
@AccessLog(title = "用户管理", businessType = BusinessTypeEnum.EXPORT)
|
||||
@PreAuthorize("@permission.has('system:user:export')")
|
||||
@GetMapping("/excel")
|
||||
public void exportUserByExcel(HttpServletResponse response, SearchUserQuery<SearchUserDO> query) {
|
||||
PageDTO<UserDTO> userList = userApplicationService.getUserList(query);
|
||||
CustomExcelUtil.writeToResponse(userList.getRows(), UserDTO.class, response);
|
||||
}
|
||||
|
||||
@Operation(summary = "用户列表导入")
|
||||
@AccessLog(title = "用户管理", businessType = BusinessTypeEnum.IMPORT)
|
||||
@PreAuthorize("@permission.has('system:user:import')")
|
||||
@PostMapping("/excel")
|
||||
public ResponseDTO<Void> importUserByExcel(MultipartFile file) {
|
||||
List<AddUserCommand> commands = CustomExcelUtil.readFromRequest(AddUserCommand.class, file);
|
||||
|
||||
for (AddUserCommand command : commands) {
|
||||
userApplicationService.addUser(command);
|
||||
}
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载批量导入模板
|
||||
*/
|
||||
@Operation(summary = "用户导入excel下载")
|
||||
@GetMapping("/excelTemplate")
|
||||
public void downloadExcelTemplate(HttpServletResponse response) {
|
||||
CustomExcelUtil.writeToResponse(ListUtil.toList(new AddUserCommand()), AddUserCommand.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户编号获取详细信息
|
||||
*/
|
||||
@Operation(summary = "用户详情")
|
||||
@PreAuthorize("@permission.has('system:user:query')")
|
||||
@GetMapping("/{userId}")
|
||||
public ResponseDTO<UserDetailDTO> getUserDetailInfo(@PathVariable(value = "userId", required = false) Long userId) {
|
||||
UserDetailDTO userDetailInfo = userApplicationService.getUserDetailInfo(userId);
|
||||
return ResponseDTO.ok(userDetailInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增用户
|
||||
*/
|
||||
@Operation(summary = "新增用户")
|
||||
@PreAuthorize("@permission.has('system:user:add') AND @dataScope.checkDeptId(#command.deptId)")
|
||||
@AccessLog(title = "用户管理", businessType = BusinessTypeEnum.ADD)
|
||||
@PostMapping
|
||||
public ResponseDTO<Void> add(@Validated @RequestBody AddUserCommand command) {
|
||||
userApplicationService.addUser(command);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改用户
|
||||
*/
|
||||
@Operation(summary = "修改用户")
|
||||
@PreAuthorize("@permission.has('system:user:edit') AND @dataScope.checkUserId(#command.userId)")
|
||||
@AccessLog(title = "用户管理", businessType = BusinessTypeEnum.MODIFY)
|
||||
@PutMapping("/{userId}")
|
||||
public ResponseDTO<Void> edit(@Validated @RequestBody UpdateUserCommand command) {
|
||||
userApplicationService.updateUser(command);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
*/
|
||||
@Operation(summary = "删除用户")
|
||||
@PreAuthorize("@permission.has('system:user:remove') AND @dataScope.checkUserIds(#userIds)")
|
||||
@AccessLog(title = "用户管理", businessType = BusinessTypeEnum.DELETE)
|
||||
@DeleteMapping("/{userIds}")
|
||||
public ResponseDTO<Void> remove(@PathVariable List<Long> userIds) {
|
||||
BulkOperationCommand<Long> bulkDeleteCommand = new BulkOperationCommand<>(userIds);
|
||||
SystemLoginUser loginUser = AuthenticationUtils.getSystemLoginUser();
|
||||
userApplicationService.deleteUsers(loginUser, bulkDeleteCommand);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置密码
|
||||
*/
|
||||
@Operation(summary = "重置用户密码")
|
||||
@PreAuthorize("@permission.has('system:user:resetPwd') AND @dataScope.checkUserId(#userId)")
|
||||
@AccessLog(title = "用户管理", businessType = BusinessTypeEnum.MODIFY)
|
||||
@PutMapping("/{userId}/password")
|
||||
public ResponseDTO<Void> resetPassword(@PathVariable Long userId, @RequestBody ResetPasswordCommand command) {
|
||||
command.setUserId(userId);
|
||||
userApplicationService.resetUserPassword(command);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态修改
|
||||
*/
|
||||
@Operation(summary = "修改用户状态")
|
||||
@PreAuthorize("@permission.has('system:user:edit') AND @dataScope.checkUserId(#command.userId)")
|
||||
@AccessLog(title = "用户管理", businessType = BusinessTypeEnum.MODIFY)
|
||||
@PutMapping("/{userId}/status")
|
||||
public ResponseDTO<Void> changeStatus(@PathVariable Long userId, @RequestBody ChangeStatusCommand command) {
|
||||
command.setUserId(userId);
|
||||
userApplicationService.changeUserStatus(command);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package com.agileboot.admin.customize.aop.accessLog;
|
||||
|
||||
import com.agileboot.common.enums.common.BusinessTypeEnum;
|
||||
import com.agileboot.common.enums.common.OperatorTypeEnum;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 自定义操作日志记录注解
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Target({ElementType.PARAMETER, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface AccessLog {
|
||||
|
||||
/**
|
||||
* 模块
|
||||
*/
|
||||
String title() default "";
|
||||
|
||||
/**
|
||||
* 功能
|
||||
*/
|
||||
BusinessTypeEnum businessType() default BusinessTypeEnum.OTHER;
|
||||
|
||||
/**
|
||||
* 操作人类别
|
||||
*/
|
||||
OperatorTypeEnum operatorType() default OperatorTypeEnum.WEB;
|
||||
|
||||
/**
|
||||
* 是否保存请求的参数
|
||||
*/
|
||||
boolean isSaveRequestData() default true;
|
||||
|
||||
/**
|
||||
* 是否保存响应的参数
|
||||
*/
|
||||
boolean isSaveResponseData() default false;
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package com.agileboot.admin.customize.aop.accessLog;
|
||||
|
||||
import com.agileboot.admin.customize.async.AsyncTaskFactory;
|
||||
import com.agileboot.infrastructure.thread.ThreadPoolManager;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.AfterReturning;
|
||||
import org.aspectj.lang.annotation.AfterThrowing;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 操作日志记录处理
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@Slf4j
|
||||
public class AccessLogAspect {
|
||||
|
||||
/**
|
||||
* 处理完请求后执行
|
||||
*
|
||||
* @param joinPoint 切点
|
||||
*/
|
||||
@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
|
||||
public void doAfterReturning(JoinPoint joinPoint, AccessLog controllerLog, Object jsonResult) {
|
||||
handleLog(joinPoint, controllerLog, null, jsonResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截异常操作
|
||||
*
|
||||
* @param joinPoint 切点
|
||||
* @param e 异常
|
||||
*/
|
||||
@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
|
||||
public void doAfterThrowing(JoinPoint joinPoint, AccessLog controllerLog, Exception e) {
|
||||
handleLog(joinPoint, controllerLog, e, null);
|
||||
}
|
||||
|
||||
protected void handleLog(final JoinPoint joinPoint, AccessLog accessLog, final Exception e, Object jsonResult) {
|
||||
try {
|
||||
OperationLogModel operationLog = new OperationLogModel();
|
||||
operationLog.fillOperatorInfo();
|
||||
operationLog.fillRequestInfo(joinPoint, accessLog, jsonResult);
|
||||
operationLog.fillStatus(e);
|
||||
operationLog.fillAccessLogInfo(accessLog);
|
||||
|
||||
// 保存数据库
|
||||
ThreadPoolManager.execute(AsyncTaskFactory.recordOperationLog(operationLog));
|
||||
} catch (Exception exp) {
|
||||
log.error("写入操作日式失败", exp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
package com.agileboot.admin.customize.aop.accessLog;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.EnumUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.agileboot.common.utils.ServletHolderUtil;
|
||||
import com.agileboot.infrastructure.user.AuthenticationUtils;
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.common.enums.common.OperationStatusEnum;
|
||||
import com.agileboot.common.enums.common.RequestMethodEnum;
|
||||
import com.agileboot.common.enums.BasicEnumUtil;
|
||||
import com.agileboot.domain.system.log.db.SysOperationLogEntity;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author valarchie
|
||||
*/
|
||||
@Slf4j
|
||||
public class OperationLogModel extends SysOperationLogEntity {
|
||||
|
||||
public static final int MAX_DATA_LENGTH = 512;
|
||||
|
||||
HttpServletRequest request = ServletHolderUtil.getRequest();
|
||||
|
||||
public void fillOperatorInfo() {
|
||||
// 获取当前的用户
|
||||
String ip = ServletHolderUtil.getClientIp();
|
||||
setOperatorIp(ip);
|
||||
SystemLoginUser loginUser = AuthenticationUtils.getSystemLoginUser();
|
||||
if (loginUser != null) {
|
||||
this.setUsername(loginUser.getUsername());
|
||||
}
|
||||
|
||||
this.setOperationTime(DateUtil.date());
|
||||
}
|
||||
|
||||
|
||||
public void fillRequestInfo(final JoinPoint joinPoint, AccessLog accessLog, Object jsonResult) {
|
||||
this.setRequestUrl(request.getRequestURI());
|
||||
// 设置方法名称
|
||||
String className = joinPoint.getTarget().getClass().getName();
|
||||
String methodName = joinPoint.getSignature().getName();
|
||||
String methodFormat = StrUtil.format("{}.{}()", className, methodName);
|
||||
this.setCalledMethod(methodFormat);
|
||||
// 设置请求方式
|
||||
RequestMethodEnum requestMethodEnum = EnumUtil.fromString(RequestMethodEnum.class,
|
||||
request.getMethod());
|
||||
this.setRequestMethod(requestMethodEnum != null ? requestMethodEnum.getValue() : RequestMethodEnum.UNKNOWN.getValue());
|
||||
|
||||
|
||||
// 是否需要保存request,参数和值
|
||||
if (accessLog.isSaveRequestData()) {
|
||||
// 获取参数的信息,传入到数据库中。
|
||||
recordRequestData(joinPoint);
|
||||
}
|
||||
// 是否需要保存response,参数和值
|
||||
if (accessLog.isSaveResponseData() && jsonResult != null) {
|
||||
this.setOperationResult(StrUtil.sub(JSONUtil.toJsonStr(jsonResult), 0, MAX_DATA_LENGTH));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void fillAccessLogInfo(AccessLog log) {
|
||||
// 设置action动作
|
||||
this.setBusinessType(log.businessType().ordinal());
|
||||
// 设置标题
|
||||
this.setRequestModule(log.title());
|
||||
// 设置操作人类别
|
||||
this.setOperatorType(log.operatorType().ordinal());
|
||||
}
|
||||
|
||||
|
||||
public void fillStatus(Exception e) {
|
||||
if (e != null) {
|
||||
this.setStatus(OperationStatusEnum.FAIL.getValue());
|
||||
this.setErrorStack(StrUtil.sub(e.getMessage(), 0, MAX_DATA_LENGTH));
|
||||
} else {
|
||||
this.setStatus(OperationStatusEnum.SUCCESS.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取请求的参数,放到log中
|
||||
*
|
||||
* @param joinPoint 方法切面
|
||||
*/
|
||||
private void recordRequestData(JoinPoint joinPoint) {
|
||||
RequestMethodEnum requestMethodEnum = BasicEnumUtil.fromValue(RequestMethodEnum.class,
|
||||
this.getRequestMethod());
|
||||
|
||||
if (requestMethodEnum == RequestMethodEnum.GET || requestMethodEnum == RequestMethodEnum.POST) {
|
||||
String params = argsArrayToString(joinPoint.getArgs());
|
||||
this.setOperationParam(StrUtil.sub(params, 0, MAX_DATA_LENGTH));
|
||||
} else {
|
||||
Map<?, ?> paramsMap = (Map<?, ?>) request
|
||||
.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
||||
this.setOperationParam(StrUtil.sub(paramsMap.toString(), 0, MAX_DATA_LENGTH));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数拼装
|
||||
*/
|
||||
private String argsArrayToString(Object[] paramsArray) {
|
||||
StringBuilder params = new StringBuilder();
|
||||
if (paramsArray != null) {
|
||||
for (Object o : paramsArray) {
|
||||
if (o != null && !isCanNotBeParseToJson(o)) {
|
||||
try {
|
||||
Object jsonObj = JSONUtil.parseObj(o);
|
||||
params.append(jsonObj).append(",");
|
||||
} catch (Exception e) {
|
||||
log.info("参数拼接错误", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return params.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否需要过滤的对象。
|
||||
*
|
||||
* @param o 对象信息。
|
||||
* @return 如果是需要过滤的对象,则返回true;否则返回false。
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public boolean isCanNotBeParseToJson(final Object o) {
|
||||
Class<?> clazz = o.getClass();
|
||||
if (clazz.isArray()) {
|
||||
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
|
||||
} else if (Collection.class.isAssignableFrom(clazz)) {
|
||||
Collection collection = (Collection) o;
|
||||
for (Object value : collection) {
|
||||
return value instanceof MultipartFile;
|
||||
}
|
||||
} else if (Map.class.isAssignableFrom(clazz)) {
|
||||
Map map = (Map) o;
|
||||
for (Object value : map.entrySet()) {
|
||||
Map.Entry entry = (Map.Entry) value;
|
||||
return entry.getValue() instanceof MultipartFile;
|
||||
}
|
||||
}
|
||||
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|
||||
|| o instanceof BindingResult;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package com.agileboot.admin.customize.async;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.agileboot.common.utils.ServletHolderUtil;
|
||||
import com.agileboot.common.utils.ip.IpRegionUtil;
|
||||
import com.agileboot.common.enums.common.LoginStatusEnum;
|
||||
import com.agileboot.domain.system.log.db.SysLoginInfoEntity;
|
||||
import com.agileboot.domain.system.log.db.SysOperationLogEntity;
|
||||
import com.agileboot.domain.system.log.db.SysLoginInfoService;
|
||||
import com.agileboot.domain.system.log.db.SysOperationLogService;
|
||||
import eu.bitwalker.useragentutils.UserAgent;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 异步工厂(产生任务用)
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Slf4j
|
||||
public class AsyncTaskFactory {
|
||||
|
||||
private AsyncTaskFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录登录信息
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param loginStatusEnum 状态
|
||||
* @param message 消息
|
||||
* @return 任务task
|
||||
*/
|
||||
public static Runnable loginInfoTask(final String username, final LoginStatusEnum loginStatusEnum, final String message) {
|
||||
// 优化一下这个类
|
||||
final UserAgent userAgent = UserAgent.parseUserAgentString(
|
||||
ServletHolderUtil.getRequest().getHeader("User-Agent"));
|
||||
// 获取客户端浏览器
|
||||
final String browser = userAgent.getBrowser() != null ? userAgent.getBrowser().getName() : "";
|
||||
final String ip = ServletHolderUtil.getClientIp();
|
||||
final String address = IpRegionUtil.getBriefLocationByIp(ip);
|
||||
// 获取客户端操作系统
|
||||
final String os = userAgent.getOperatingSystem() != null ? userAgent.getOperatingSystem().getName() : "";
|
||||
|
||||
log.info("ip: {}, address: {}, username: {}, loginStatusEnum: {}, message: {}", ip, address, username,
|
||||
loginStatusEnum, message);
|
||||
return () -> {
|
||||
// 封装对象
|
||||
SysLoginInfoEntity loginInfo = new SysLoginInfoEntity();
|
||||
loginInfo.setUsername(username);
|
||||
loginInfo.setIpAddress(ip);
|
||||
loginInfo.setLoginLocation(address);
|
||||
loginInfo.setBrowser(browser);
|
||||
loginInfo.setOperationSystem(os);
|
||||
loginInfo.setMsg(message);
|
||||
loginInfo.setLoginTime(DateUtil.date());
|
||||
loginInfo.setStatus(loginStatusEnum.getValue());
|
||||
// 插入数据
|
||||
SpringUtil.getBean(SysLoginInfoService.class).save(loginInfo);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作日志记录
|
||||
*
|
||||
* @param operationLog 操作日志信息
|
||||
* @return 任务task
|
||||
*/
|
||||
public static Runnable recordOperationLog(final SysOperationLogEntity operationLog) {
|
||||
return () -> {
|
||||
// 远程查询操作地点
|
||||
operationLog.setOperatorLocation(IpRegionUtil.getBriefLocationByIp(operationLog.getOperatorIp()));
|
||||
SpringUtil.getBean(SysOperationLogService.class).save(operationLog);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package com.agileboot.admin.customize.config;
|
||||
|
||||
import com.agileboot.infrastructure.user.AuthenticationUtils;
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.admin.customize.service.login.TokenService;
|
||||
import java.io.IOException;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
/**
|
||||
* token过滤器 验证token有效性
|
||||
* 继承OncePerRequestFilter类的话 可以确保只执行filter一次, 避免执行多次
|
||||
* @author valarchie
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||
|
||||
private final TokenService tokenService;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws ServletException, IOException {
|
||||
SystemLoginUser loginUser = tokenService.getLoginUser(request);
|
||||
if (loginUser != null && AuthenticationUtils.getAuthentication() == null) {
|
||||
tokenService.refreshToken(loginUser);
|
||||
// 如果没有将当前登录用户放入到上下文中的话,会认定用户未授权,返回用户未登陆的错误
|
||||
putCurrentLoginUserIntoContext(request, loginUser);
|
||||
|
||||
log.debug("request process in jwt token filter. get login user id: {}", loginUser.getUserId());
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
|
||||
private void putCurrentLoginUserIntoContext(HttpServletRequest request, SystemLoginUser loginUser) {
|
||||
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(loginUser,
|
||||
null, loginUser.getAuthorities());
|
||||
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
SecurityContextHolder.getContext().setAuthentication(authToken);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
package com.agileboot.admin.customize.config;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.agileboot.admin.customize.service.login.LoginService;
|
||||
import com.agileboot.common.core.dto.ResponseDTO;
|
||||
import com.agileboot.common.exception.ApiException;
|
||||
import com.agileboot.common.exception.error.ErrorCode.Client;
|
||||
import com.agileboot.common.utils.ServletHolderUtil;
|
||||
import com.agileboot.domain.common.cache.RedisCacheService;
|
||||
import com.agileboot.admin.customize.async.AsyncTaskFactory;
|
||||
import com.agileboot.infrastructure.thread.ThreadPoolManager;
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.admin.customize.service.login.TokenService;
|
||||
import com.agileboot.common.enums.common.LoginStatusEnum;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
/**
|
||||
* 主要配置登录流程逻辑涉及以下几个类
|
||||
* @see UserDetailsServiceImpl#loadUserByUsername 用于登录流程通过用户名加载用户
|
||||
* @see this#unauthorizedHandler() 用于用户未授权或登录失败处理
|
||||
* @see this#logOutSuccessHandler 用于退出登录成功后的逻辑
|
||||
* @see JwtAuthenticationTokenFilter#doFilter token的校验和刷新
|
||||
* @see LoginService#login 登录逻辑
|
||||
* @author valarchie
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
@RequiredArgsConstructor
|
||||
public class SecurityConfig {
|
||||
|
||||
private final TokenService tokenService;
|
||||
|
||||
private final RedisCacheService redisCache;
|
||||
|
||||
/**
|
||||
* token认证过滤器
|
||||
*/
|
||||
private final JwtAuthenticationTokenFilter jwtTokenFilter;
|
||||
|
||||
private final UserDetailsService userDetailsService;
|
||||
|
||||
/**
|
||||
* 跨域过滤器
|
||||
*/
|
||||
private final CorsFilter corsFilter;
|
||||
|
||||
|
||||
/**
|
||||
* 登录异常处理类
|
||||
* 用户未登陆的话 在这个Bean中处理
|
||||
*/
|
||||
@Bean
|
||||
public AuthenticationEntryPoint unauthorizedHandler() {
|
||||
return (request, response, exception) -> {
|
||||
ResponseDTO<Object> responseDTO = ResponseDTO.fail(
|
||||
new ApiException(Client.COMMON_NO_AUTHORIZATION, request.getRequestURI())
|
||||
);
|
||||
ServletHolderUtil.renderString(response, JSONUtil.toJsonStr(responseDTO));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 退出成功处理类 返回成功
|
||||
* 在SecurityConfig类当中 定义了/logout 路径对应处理逻辑
|
||||
*/
|
||||
@Bean
|
||||
public LogoutSuccessHandler logOutSuccessHandler() {
|
||||
return (request, response, authentication) -> {
|
||||
SystemLoginUser loginUser = tokenService.getLoginUser(request);
|
||||
if (loginUser != null) {
|
||||
String userName = loginUser.getUsername();
|
||||
// 删除用户缓存记录
|
||||
redisCache.loginUserCache.delete(loginUser.getCachedKey());
|
||||
// 记录用户退出日志
|
||||
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(
|
||||
userName, LoginStatusEnum.LOGOUT, LoginStatusEnum.LOGOUT.description()));
|
||||
}
|
||||
ServletHolderUtil.renderString(response, JSONUtil.toJsonStr(ResponseDTO.ok()));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 强散列哈希加密实现
|
||||
*/
|
||||
@Bean
|
||||
public BCryptPasswordEncoder bCryptPasswordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 鉴权管理类
|
||||
* @see UserDetailsServiceImpl#loadUserByUsername
|
||||
*/
|
||||
@Bean
|
||||
public AuthenticationManager authManager(HttpSecurity http) throws Exception {
|
||||
return http.getSharedObject(AuthenticationManagerBuilder.class)
|
||||
.userDetailsService(userDetailsService)
|
||||
.passwordEncoder(bCryptPasswordEncoder())
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
|
||||
httpSecurity
|
||||
// CSRF禁用,因为不使用session
|
||||
.csrf().disable()
|
||||
// 认证失败处理类
|
||||
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler()).and()
|
||||
// 基于token,所以不需要session
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
|
||||
// 过滤请求
|
||||
.authorizeRequests()
|
||||
// 对于登录login 注册register 验证码captchaImage 以及公共Api的请求允许匿名访问
|
||||
// 注意: 当携带token请求以下这几个接口时 会返回403的错误
|
||||
.antMatchers("/login", "/register", "/getConfig", "/captchaImage", "/api/**").anonymous()
|
||||
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js",
|
||||
"/profile/**").permitAll()
|
||||
// TODO this is danger.
|
||||
.antMatchers("/swagger-ui.html").anonymous()
|
||||
.antMatchers("/swagger-resources/**").anonymous()
|
||||
.antMatchers("/webjars/**").anonymous()
|
||||
.antMatchers("/*/api-docs","/*/api-docs/swagger-config").anonymous()
|
||||
.antMatchers("/**/api-docs.yaml" ).anonymous()
|
||||
.antMatchers("/druid/**").anonymous()
|
||||
// 除上面外的所有请求全部需要鉴权认证
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
// 禁用 X-Frame-Options 响应头。下面是具体解释:
|
||||
// X-Frame-Options 是一个 HTTP 响应头,用于防止网页被嵌入到其他网页的 <frame>、<iframe> 或 <object> 标签中,从而可以减少点击劫持攻击的风险
|
||||
.headers().frameOptions().disable();
|
||||
httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logOutSuccessHandler());
|
||||
// 添加JWT filter 需要一开始就通过token识别出登录用户 并放到上下文中 所以jwtFilter需要放前面
|
||||
httpSecurity.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
// 添加CORS filter
|
||||
httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
|
||||
httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
|
||||
|
||||
return httpSecurity.build();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
package com.agileboot.admin.customize.service.login;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.img.ImgUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.KeyType;
|
||||
import com.agileboot.common.utils.ServletHolderUtil;
|
||||
import com.agileboot.common.config.AgileBootConfig;
|
||||
import com.agileboot.common.constant.Constants.Captcha;
|
||||
import com.agileboot.common.exception.ApiException;
|
||||
import com.agileboot.common.exception.error.ErrorCode;
|
||||
import com.agileboot.common.exception.error.ErrorCode.Business;
|
||||
import com.agileboot.common.utils.ServletHolderUtil;
|
||||
import com.agileboot.common.utils.i18n.MessageUtils;
|
||||
import com.agileboot.domain.common.cache.GuavaCacheService;
|
||||
import com.agileboot.domain.common.cache.MapCache;
|
||||
import com.agileboot.domain.common.cache.RedisCacheService;
|
||||
import com.agileboot.admin.customize.async.AsyncTaskFactory;
|
||||
import com.agileboot.infrastructure.thread.ThreadPoolManager;
|
||||
import com.agileboot.admin.customize.service.login.dto.CaptchaDTO;
|
||||
import com.agileboot.admin.customize.service.login.dto.ConfigDTO;
|
||||
import com.agileboot.admin.customize.service.login.command.LoginCommand;
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.common.enums.common.ConfigKeyEnum;
|
||||
import com.agileboot.common.enums.common.LoginStatusEnum;
|
||||
import com.agileboot.domain.system.user.db.SysUserEntity;
|
||||
import com.google.code.kaptcha.Producer;
|
||||
import java.awt.image.BufferedImage;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.FastByteArrayOutputStream;
|
||||
|
||||
/**
|
||||
* 登录校验方法
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class LoginService {
|
||||
|
||||
private final TokenService tokenService;
|
||||
|
||||
private final RedisCacheService redisCache;
|
||||
|
||||
private final GuavaCacheService guavaCache;
|
||||
|
||||
private final AuthenticationManager authenticationManager;
|
||||
|
||||
@Resource(name = "captchaProducer")
|
||||
private Producer captchaProducer;
|
||||
|
||||
@Resource(name = "captchaProducerMath")
|
||||
private Producer captchaProducerMath;
|
||||
|
||||
/**
|
||||
* 登录验证
|
||||
*
|
||||
* @param loginCommand 登录参数
|
||||
* @return 结果
|
||||
*/
|
||||
public String login(LoginCommand loginCommand) {
|
||||
// 验证码开关
|
||||
if (isCaptchaOn()) {
|
||||
validateCaptcha(loginCommand.getUsername(), loginCommand.getCaptchaCode(), loginCommand.getCaptchaCodeKey());
|
||||
}
|
||||
// 用户验证
|
||||
Authentication authentication;
|
||||
String decryptPassword = decryptPassword(loginCommand.getPassword());
|
||||
try {
|
||||
// 该方法会去调用UserDetailsServiceImpl#loadUserByUsername 校验用户名和密码 认证鉴权
|
||||
authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
|
||||
loginCommand.getUsername(), decryptPassword));
|
||||
} catch (BadCredentialsException e) {
|
||||
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(loginCommand.getUsername(), LoginStatusEnum.LOGIN_FAIL,
|
||||
MessageUtils.message("Business.LOGIN_WRONG_USER_PASSWORD")));
|
||||
throw new ApiException(e, ErrorCode.Business.LOGIN_WRONG_USER_PASSWORD);
|
||||
} catch (AuthenticationException e) {
|
||||
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(loginCommand.getUsername(), LoginStatusEnum.LOGIN_FAIL, e.getMessage()));
|
||||
throw new ApiException(e, ErrorCode.Business.LOGIN_ERROR, e.getMessage());
|
||||
} catch (Exception e) {
|
||||
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(loginCommand.getUsername(), LoginStatusEnum.LOGIN_FAIL, e.getMessage()));
|
||||
throw new ApiException(e, Business.LOGIN_ERROR, e.getMessage());
|
||||
}
|
||||
// 把当前登录用户 放入上下文中
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
// 这里获取的loginUser是UserDetailsServiceImpl#loadUserByUsername方法返回的LoginUser
|
||||
SystemLoginUser loginUser = (SystemLoginUser) authentication.getPrincipal();
|
||||
recordLoginInfo(loginUser);
|
||||
// 生成token
|
||||
return tokenService.createTokenAndPutUserInCache(loginUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证码 data
|
||||
*
|
||||
* @return {@link ConfigDTO}
|
||||
*/
|
||||
public ConfigDTO getConfig() {
|
||||
ConfigDTO configDTO = new ConfigDTO();
|
||||
|
||||
boolean isCaptchaOn = isCaptchaOn();
|
||||
configDTO.setIsCaptchaOn(isCaptchaOn);
|
||||
configDTO.setDictionary(MapCache.dictionaryCache());
|
||||
return configDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证码 data
|
||||
*
|
||||
* @return 验证码
|
||||
*/
|
||||
public CaptchaDTO generateCaptchaImg() {
|
||||
CaptchaDTO captchaDTO = new CaptchaDTO();
|
||||
|
||||
boolean isCaptchaOn = isCaptchaOn();
|
||||
captchaDTO.setIsCaptchaOn(isCaptchaOn);
|
||||
|
||||
if (isCaptchaOn) {
|
||||
String expression;
|
||||
String answer = null;
|
||||
BufferedImage image = null;
|
||||
|
||||
// 生成验证码
|
||||
String captchaType = AgileBootConfig.getCaptchaType();
|
||||
if (Captcha.MATH_TYPE.equals(captchaType)) {
|
||||
String capText = captchaProducerMath.createText();
|
||||
String[] expressionAndAnswer = capText.split("@");
|
||||
expression = expressionAndAnswer[0];
|
||||
answer = expressionAndAnswer[1];
|
||||
image = captchaProducerMath.createImage(expression);
|
||||
}
|
||||
|
||||
if (Captcha.CHAR_TYPE.equals(captchaType)) {
|
||||
expression = answer = captchaProducer.createText();
|
||||
image = captchaProducer.createImage(expression);
|
||||
}
|
||||
|
||||
if (image == null) {
|
||||
throw new ApiException(ErrorCode.Internal.LOGIN_CAPTCHA_GENERATE_FAIL);
|
||||
}
|
||||
|
||||
// 保存验证码信息
|
||||
String imgKey = IdUtil.simpleUUID();
|
||||
|
||||
redisCache.captchaCache.set(imgKey, answer);
|
||||
// 转换流信息写出
|
||||
FastByteArrayOutputStream os = new FastByteArrayOutputStream();
|
||||
ImgUtil.writeJpg(image, os);
|
||||
|
||||
captchaDTO.setCaptchaCodeKey(imgKey);
|
||||
captchaDTO.setCaptchaCodeImg(Base64.encode(os.toByteArray()));
|
||||
|
||||
}
|
||||
|
||||
return captchaDTO;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 校验验证码
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param captchaCode 验证码
|
||||
* @param captchaCodeKey 验证码对应的缓存key
|
||||
*/
|
||||
public void validateCaptcha(String username, String captchaCode, String captchaCodeKey) {
|
||||
String captcha = redisCache.captchaCache.getObjectById(captchaCodeKey);
|
||||
redisCache.captchaCache.delete(captchaCodeKey);
|
||||
if (captcha == null) {
|
||||
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(username, LoginStatusEnum.LOGIN_FAIL,
|
||||
ErrorCode.Business.LOGIN_CAPTCHA_CODE_EXPIRE.message()));
|
||||
throw new ApiException(ErrorCode.Business.LOGIN_CAPTCHA_CODE_EXPIRE);
|
||||
}
|
||||
if (!captchaCode.equalsIgnoreCase(captcha)) {
|
||||
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(username, LoginStatusEnum.LOGIN_FAIL,
|
||||
ErrorCode.Business.LOGIN_CAPTCHA_CODE_WRONG.message()));
|
||||
throw new ApiException(ErrorCode.Business.LOGIN_CAPTCHA_CODE_WRONG);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录登录信息
|
||||
* @param loginUser 登录用户
|
||||
*/
|
||||
public void recordLoginInfo(SystemLoginUser loginUser) {
|
||||
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(loginUser.getUsername(), LoginStatusEnum.LOGIN_SUCCESS,
|
||||
LoginStatusEnum.LOGIN_SUCCESS.description()));
|
||||
|
||||
SysUserEntity entity = redisCache.userCache.getObjectById(loginUser.getUserId());
|
||||
|
||||
entity.setLoginIp(ServletHolderUtil.getClientIp());
|
||||
entity.setLoginDate(DateUtil.date());
|
||||
entity.updateById();
|
||||
}
|
||||
|
||||
public String decryptPassword(String originalPassword) {
|
||||
byte[] decryptBytes = SecureUtil.rsa(AgileBootConfig.getRsaPrivateKey(), null)
|
||||
.decrypt(Base64.decode(originalPassword), KeyType.PrivateKey);
|
||||
|
||||
return StrUtil.str(decryptBytes, CharsetUtil.CHARSET_UTF_8);
|
||||
}
|
||||
|
||||
private boolean isCaptchaOn() {
|
||||
return Convert.toBool(guavaCache.configCache.get(ConfigKeyEnum.CAPTCHA.getValue()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
package com.agileboot.admin.customize.service.login;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.agileboot.common.constant.Constants.Token;
|
||||
import com.agileboot.common.exception.ApiException;
|
||||
import com.agileboot.common.exception.error.ErrorCode;
|
||||
import com.agileboot.domain.common.cache.RedisCacheService;
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.MalformedJwtException;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import io.jsonwebtoken.UnsupportedJwtException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* token验证处理
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
@Data
|
||||
@RequiredArgsConstructor
|
||||
public class TokenService {
|
||||
|
||||
/**
|
||||
* 自定义令牌标识
|
||||
*/
|
||||
@Value("${token.header}")
|
||||
private String header;
|
||||
|
||||
/**
|
||||
* 令牌秘钥
|
||||
*/
|
||||
@Value("${token.secret}")
|
||||
private String secret;
|
||||
|
||||
/**
|
||||
* 自动刷新token的时间,当过期时间不足autoRefreshTime的值的时候,会触发刷新用户登录缓存的时间
|
||||
* 比如这个值是20, 用户是8点登录的, 8点半缓存会过期, 当过8.10分的时候,就少于20分钟了,便触发
|
||||
* 刷新登录用户的缓存时间
|
||||
*/
|
||||
@Value("${token.autoRefreshTime}")
|
||||
private long autoRefreshTime;
|
||||
|
||||
private final RedisCacheService redisCache;
|
||||
|
||||
/**
|
||||
* 获取用户身份信息
|
||||
*
|
||||
* @return 用户信息
|
||||
*/
|
||||
public SystemLoginUser getLoginUser(HttpServletRequest request) {
|
||||
// 获取请求携带的令牌
|
||||
String token = getTokenFromRequest(request);
|
||||
if (StrUtil.isNotEmpty(token)) {
|
||||
try {
|
||||
Claims claims = parseToken(token);
|
||||
// 解析对应的权限以及用户信息
|
||||
String uuid = (String) claims.get(Token.LOGIN_USER_KEY);
|
||||
|
||||
return redisCache.loginUserCache.getObjectOnlyInCacheById(uuid);
|
||||
} catch (SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException jwtException) {
|
||||
log.error("parse token failed.", jwtException);
|
||||
throw new ApiException(jwtException, ErrorCode.Client.INVALID_TOKEN);
|
||||
} catch (Exception e) {
|
||||
log.error("fail to get cached user from redis", e);
|
||||
throw new ApiException(e, ErrorCode.Client.TOKEN_PROCESS_FAILED, e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建令牌
|
||||
*
|
||||
* @param loginUser 用户信息
|
||||
* @return 令牌
|
||||
*/
|
||||
public String createTokenAndPutUserInCache(SystemLoginUser loginUser) {
|
||||
loginUser.setCachedKey(IdUtil.fastUUID());
|
||||
|
||||
redisCache.loginUserCache.set(loginUser.getCachedKey(), loginUser);
|
||||
|
||||
return generateToken(MapUtil.of(Token.LOGIN_USER_KEY, loginUser.getCachedKey()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 当超过20分钟,自动刷新token
|
||||
* @param loginUser 登录用户
|
||||
*/
|
||||
public void refreshToken(SystemLoginUser loginUser) {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (currentTime > loginUser.getAutoRefreshCacheTime()) {
|
||||
loginUser.setAutoRefreshCacheTime(currentTime + TimeUnit.MINUTES.toMillis(autoRefreshTime));
|
||||
// 根据uuid将loginUser存入缓存
|
||||
redisCache.loginUserCache.set(loginUser.getCachedKey(), loginUser);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从数据声明生成令牌
|
||||
*
|
||||
* @param claims 数据声明
|
||||
* @return 令牌
|
||||
*/
|
||||
private String generateToken(Map<String, Object> claims) {
|
||||
return Jwts.builder()
|
||||
.setClaims(claims)
|
||||
.signWith(SignatureAlgorithm.HS512, secret).compact();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从令牌中获取数据声明
|
||||
*
|
||||
* @param token 令牌
|
||||
* @return 数据声明
|
||||
*/
|
||||
private Claims parseToken(String token) {
|
||||
return Jwts.parser()
|
||||
.setSigningKey(secret)
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从令牌中获取用户名
|
||||
*
|
||||
* @param token 令牌
|
||||
* @return 用户名
|
||||
*/
|
||||
private String getUsernameFromToken(String token) {
|
||||
Claims claims = parseToken(token);
|
||||
return claims.getSubject();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求token
|
||||
*
|
||||
* @return token
|
||||
*/
|
||||
private String getTokenFromRequest(HttpServletRequest request) {
|
||||
String token = request.getHeader(header);
|
||||
if (StrUtil.isNotEmpty(token) && token.startsWith(Token.PREFIX)) {
|
||||
token = StrUtil.stripIgnoreCase(token, Token.PREFIX, null);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
package com.agileboot.admin.customize.service.login;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import java.util.Collections;
|
||||
import com.agileboot.common.exception.ApiException;
|
||||
import com.agileboot.common.exception.error.ErrorCode;
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.infrastructure.user.web.RoleInfo;
|
||||
import com.agileboot.infrastructure.user.web.DataScopeEnum;
|
||||
import com.agileboot.common.enums.common.UserStatusEnum;
|
||||
import com.agileboot.common.enums.BasicEnumUtil;
|
||||
import com.agileboot.domain.system.menu.db.SysMenuEntity;
|
||||
import com.agileboot.domain.system.role.db.SysRoleEntity;
|
||||
import com.agileboot.domain.system.user.db.SysUserEntity;
|
||||
import com.agileboot.domain.system.menu.db.SysMenuService;
|
||||
import com.agileboot.domain.system.role.db.SysRoleService;
|
||||
import com.agileboot.domain.system.user.db.SysUserService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 自定义加载用户信息通过用户名
|
||||
* 用于SpringSecurity 登录流程
|
||||
* 没有办法把这个类 放进loginService中 会在SecurityConfig中造成循环依赖
|
||||
* @see com.agileboot.infrastructure.config.SecurityConfig#filterChain(HttpSecurity)
|
||||
* @author valarchie
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class UserDetailsServiceImpl implements UserDetailsService {
|
||||
|
||||
private final SysUserService userService;
|
||||
|
||||
private final SysMenuService menuService;
|
||||
|
||||
private final SysRoleService roleService;
|
||||
|
||||
private final TokenService tokenService;
|
||||
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
SysUserEntity userEntity = userService.getUserByUserName(username);
|
||||
if (userEntity == null) {
|
||||
log.info("登录用户:{} 不存在.", username);
|
||||
throw new ApiException(ErrorCode.Business.USER_NON_EXIST, username);
|
||||
}
|
||||
if (!Objects.equals(UserStatusEnum.NORMAL.getValue(), userEntity.getStatus())) {
|
||||
log.info("登录用户:{} 已被停用.", username);
|
||||
throw new ApiException(ErrorCode.Business.USER_IS_DISABLE, username);
|
||||
}
|
||||
|
||||
RoleInfo roleInfo = getRoleInfo(userEntity.getRoleId(), userEntity.getIsAdmin());
|
||||
|
||||
SystemLoginUser loginUser = new SystemLoginUser(userEntity.getUserId(), userEntity.getIsAdmin(), userEntity.getUsername(),
|
||||
userEntity.getPassword(), roleInfo, userEntity.getDeptId());
|
||||
loginUser.fillLoginInfo();
|
||||
loginUser.setAutoRefreshCacheTime(loginUser.getLoginInfo().getLoginTime()
|
||||
+ TimeUnit.MINUTES.toMillis(tokenService.getAutoRefreshTime()));
|
||||
return loginUser;
|
||||
}
|
||||
|
||||
public RoleInfo getRoleInfo(Long roleId, boolean isAdmin) {
|
||||
if (roleId == null) {
|
||||
return RoleInfo.EMPTY_ROLE;
|
||||
}
|
||||
|
||||
if (isAdmin) {
|
||||
LambdaQueryWrapper<SysMenuEntity> menuQuery = Wrappers.lambdaQuery();
|
||||
menuQuery.select(SysMenuEntity::getMenuId);
|
||||
List<SysMenuEntity> allMenus = menuService.list(menuQuery);
|
||||
|
||||
Set<Long> allMenuIds = allMenus.stream().map(SysMenuEntity::getMenuId).collect(Collectors.toSet());
|
||||
|
||||
return new RoleInfo(RoleInfo.ADMIN_ROLE_ID, RoleInfo.ADMIN_ROLE_KEY, DataScopeEnum.ALL, Collections.emptySet(),
|
||||
RoleInfo.ADMIN_PERMISSIONS, allMenuIds);
|
||||
|
||||
}
|
||||
|
||||
SysRoleEntity roleEntity = roleService.getById(roleId);
|
||||
|
||||
if (roleEntity == null) {
|
||||
return RoleInfo.EMPTY_ROLE;
|
||||
}
|
||||
|
||||
List<SysMenuEntity> menuList = roleService.getMenuListByRoleId(roleId);
|
||||
|
||||
Set<Long> menuIds = menuList.stream().map(SysMenuEntity::getMenuId).collect(Collectors.toSet());
|
||||
Set<String> permissions = menuList.stream().map(SysMenuEntity::getPermission).collect(Collectors.toSet());
|
||||
|
||||
DataScopeEnum dataScopeEnum = BasicEnumUtil.fromValue(DataScopeEnum.class, roleEntity.getDataScope());
|
||||
|
||||
Set<Long> deptIdSet = Collections.emptySet();
|
||||
if (StrUtil.isNotEmpty(roleEntity.getDeptIdSet())) {
|
||||
deptIdSet = StrUtil.split(roleEntity.getDeptIdSet(), ",").stream()
|
||||
.map(Convert::toLong).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
return new RoleInfo(roleId, roleEntity.getRoleKey(), dataScopeEnum, deptIdSet, permissions, menuIds);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.agileboot.admin.customize.service.login.command;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 用户登录对象
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Data
|
||||
public class LoginCommand {
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 用户密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
private String captchaCode;
|
||||
|
||||
/**
|
||||
* 唯一标识
|
||||
*/
|
||||
private String captchaCodeKey;
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package com.agileboot.admin.customize.service.login.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author valarchie
|
||||
*/
|
||||
@Data
|
||||
public class CaptchaDTO {
|
||||
|
||||
private Boolean isCaptchaOn;
|
||||
private String captchaCodeKey;
|
||||
private String captchaCodeImg;
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.agileboot.admin.customize.service.login.dto;
|
||||
|
||||
import com.agileboot.common.enums.dictionary.DictionaryData;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author valarchie
|
||||
*/
|
||||
@Data
|
||||
public class ConfigDTO {
|
||||
|
||||
private Boolean isCaptchaOn;
|
||||
|
||||
private Map<String, List<DictionaryData>> dictionary;
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package com.agileboot.admin.customize.service.permission;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.agileboot.admin.customize.service.permission.model.AbstractDataPermissionChecker;
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.admin.customize.service.permission.model.checker.AllDataPermissionChecker;
|
||||
import com.agileboot.admin.customize.service.permission.model.checker.CustomDataPermissionChecker;
|
||||
import com.agileboot.admin.customize.service.permission.model.checker.DefaultDataPermissionChecker;
|
||||
import com.agileboot.admin.customize.service.permission.model.checker.DeptTreeDataPermissionChecker;
|
||||
import com.agileboot.admin.customize.service.permission.model.checker.OnlySelfDataPermissionChecker;
|
||||
import com.agileboot.admin.customize.service.permission.model.checker.SingleDeptDataPermissionChecker;
|
||||
import com.agileboot.infrastructure.user.web.DataScopeEnum;
|
||||
import com.agileboot.domain.system.dept.db.SysDeptService;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 数据权限检测器工厂
|
||||
* @author valarchie
|
||||
*/
|
||||
@Component
|
||||
public class DataPermissionCheckerFactory {
|
||||
private static AbstractDataPermissionChecker allChecker;
|
||||
private static AbstractDataPermissionChecker customChecker;
|
||||
private static AbstractDataPermissionChecker singleDeptChecker;
|
||||
private static AbstractDataPermissionChecker deptTreeChecker;
|
||||
private static AbstractDataPermissionChecker onlySelfChecker;
|
||||
private static AbstractDataPermissionChecker defaultSelfChecker;
|
||||
|
||||
|
||||
@PostConstruct
|
||||
public void initAllChecker() {
|
||||
SysDeptService deptService = SpringUtil.getBean(SysDeptService.class);
|
||||
|
||||
allChecker = new AllDataPermissionChecker();
|
||||
customChecker = new CustomDataPermissionChecker(deptService);
|
||||
singleDeptChecker = new SingleDeptDataPermissionChecker(deptService);
|
||||
deptTreeChecker = new DeptTreeDataPermissionChecker(deptService);
|
||||
onlySelfChecker = new OnlySelfDataPermissionChecker(deptService);
|
||||
defaultSelfChecker = new DefaultDataPermissionChecker();
|
||||
}
|
||||
|
||||
|
||||
public static AbstractDataPermissionChecker getChecker(SystemLoginUser loginUser) {
|
||||
if (loginUser == null) {
|
||||
return deptTreeChecker;
|
||||
}
|
||||
|
||||
DataScopeEnum dataScope = loginUser.getRoleInfo().getDataScope();
|
||||
switch (dataScope) {
|
||||
case ALL:
|
||||
return allChecker;
|
||||
case CUSTOM_DEFINE:
|
||||
return customChecker;
|
||||
case SINGLE_DEPT:
|
||||
return singleDeptChecker;
|
||||
case DEPT_TREE:
|
||||
return deptTreeChecker;
|
||||
case ONLY_SELF:
|
||||
return onlySelfChecker;
|
||||
default:
|
||||
return defaultSelfChecker;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package com.agileboot.admin.customize.service.permission;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.agileboot.admin.customize.service.permission.model.AbstractDataPermissionChecker;
|
||||
import com.agileboot.admin.customize.service.permission.model.DataCondition;
|
||||
import com.agileboot.infrastructure.user.AuthenticationUtils;
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.domain.system.user.db.SysUserEntity;
|
||||
import com.agileboot.domain.system.user.db.SysUserService;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 数据权限校验服务
|
||||
* @author valarchie
|
||||
*/
|
||||
@Service("dataScope")
|
||||
@RequiredArgsConstructor
|
||||
public class DataPermissionService {
|
||||
|
||||
private final SysUserService userService;
|
||||
|
||||
/**
|
||||
* 通过userId 校验当前用户 对 目标用户是否有操作权限
|
||||
*
|
||||
* @param userId 用户id
|
||||
* @return 检验结果
|
||||
*/
|
||||
public boolean checkUserId(Long userId) {
|
||||
SystemLoginUser loginUser = AuthenticationUtils.getSystemLoginUser();
|
||||
SysUserEntity targetUser = userService.getById(userId);
|
||||
if (targetUser == null) {
|
||||
return true;
|
||||
}
|
||||
return checkDataScope(loginUser, targetUser.getDeptId(), userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过userId 校验当前用户 对 目标用户是否有操作权限
|
||||
* @param userIds 用户id列表
|
||||
* @return 校验结果
|
||||
*/
|
||||
public boolean checkUserIds(List<Long> userIds) {
|
||||
if (CollUtil.isNotEmpty(userIds)) {
|
||||
for (Long userId : userIds) {
|
||||
boolean checkResult = checkUserId(userId);
|
||||
if (!checkResult) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean checkDeptId(Long deptId) {
|
||||
SystemLoginUser loginUser = AuthenticationUtils.getSystemLoginUser();
|
||||
return checkDataScope(loginUser, deptId, null);
|
||||
}
|
||||
|
||||
|
||||
public boolean checkDataScope(SystemLoginUser loginUser, Long targetDeptId, Long targetUserId) {
|
||||
DataCondition dataCondition = DataCondition.builder().targetDeptId(targetDeptId).targetUserId(targetUserId).build();
|
||||
AbstractDataPermissionChecker checker = DataPermissionCheckerFactory.getChecker(loginUser);
|
||||
return checker.check(loginUser, dataCondition);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package com.agileboot.admin.customize.service.permission;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.agileboot.infrastructure.user.AuthenticationUtils;
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.infrastructure.user.web.RoleInfo;
|
||||
import java.util.Set;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Service("permission")
|
||||
public class MenuPermissionService {
|
||||
|
||||
|
||||
/**
|
||||
* 验证用户是否具备某权限
|
||||
*
|
||||
* @param permission 权限字符串
|
||||
* @return 用户是否具备某权限
|
||||
*/
|
||||
public boolean has(String permission) {
|
||||
if (StrUtil.isEmpty(permission)) {
|
||||
return false;
|
||||
}
|
||||
SystemLoginUser loginUser = AuthenticationUtils.getSystemLoginUser();
|
||||
if (loginUser == null || CollUtil.isEmpty(loginUser.getRoleInfo().getMenuPermissions())) {
|
||||
return false;
|
||||
}
|
||||
return has(loginUser.getRoleInfo().getMenuPermissions(), permission);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断是否包含权限
|
||||
*
|
||||
* @param permissions 权限列表
|
||||
* @param permission 权限字符串
|
||||
* @return 用户是否具备某权限
|
||||
*/
|
||||
private boolean has(Set<String> permissions, String permission) {
|
||||
return permissions.contains(RoleInfo.ALL_PERMISSIONS) || permissions.contains(StrUtil.trim(permission));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.agileboot.admin.customize.service.permission.model;
|
||||
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.domain.system.dept.db.SysDeptService;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 数据权限测试接口
|
||||
* @author valarchie
|
||||
*/
|
||||
@Data
|
||||
public abstract class AbstractDataPermissionChecker {
|
||||
|
||||
private SysDeptService deptService;
|
||||
|
||||
/**
|
||||
* 检测当前用户对于 给定条件的数据 是否有权限
|
||||
*
|
||||
* @param loginUser 登录用户
|
||||
* @param condition 条件
|
||||
* @return 校验结果
|
||||
*/
|
||||
public abstract boolean check(SystemLoginUser loginUser, DataCondition condition);
|
||||
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.agileboot.admin.customize.service.permission.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author valarchie
|
||||
* 供 DataPermissionChecker使用的 数据条件
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DataCondition {
|
||||
|
||||
private Long targetDeptId;
|
||||
private Long targetUserId;
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.agileboot.admin.customize.service.permission.model.checker;
|
||||
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.admin.customize.service.permission.model.AbstractDataPermissionChecker;
|
||||
import com.agileboot.admin.customize.service.permission.model.DataCondition;
|
||||
import com.agileboot.domain.system.dept.db.SysDeptService;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 数据权限测试接口
|
||||
* @author valarchie
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class AllDataPermissionChecker extends AbstractDataPermissionChecker {
|
||||
|
||||
private SysDeptService deptService;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean check(SystemLoginUser loginUser, DataCondition condition) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package com.agileboot.admin.customize.service.permission.model.checker;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.admin.customize.service.permission.model.AbstractDataPermissionChecker;
|
||||
import com.agileboot.admin.customize.service.permission.model.DataCondition;
|
||||
import com.agileboot.domain.system.dept.db.SysDeptService;
|
||||
import java.util.Set;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 数据权限测试接口
|
||||
* @author valarchie
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class CustomDataPermissionChecker extends AbstractDataPermissionChecker {
|
||||
|
||||
private SysDeptService deptService;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean check(SystemLoginUser loginUser, DataCondition condition) {
|
||||
if (condition == null || loginUser == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (loginUser.getRoleInfo() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Set<Long> deptIdSet = loginUser.getRoleInfo().getDeptIdSet();
|
||||
Long targetDeptId = condition.getTargetDeptId();
|
||||
|
||||
return condition.getTargetDeptId() != null && CollUtil.safeContains(deptIdSet, targetDeptId);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.agileboot.admin.customize.service.permission.model.checker;
|
||||
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.admin.customize.service.permission.model.AbstractDataPermissionChecker;
|
||||
import com.agileboot.admin.customize.service.permission.model.DataCondition;
|
||||
import com.agileboot.domain.system.dept.db.SysDeptService;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 数据权限测试接口
|
||||
* @author valarchie
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class DefaultDataPermissionChecker extends AbstractDataPermissionChecker {
|
||||
|
||||
private SysDeptService deptService;
|
||||
|
||||
@Override
|
||||
public boolean check(SystemLoginUser loginUser, DataCondition condition) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package com.agileboot.admin.customize.service.permission.model.checker;
|
||||
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.admin.customize.service.permission.model.AbstractDataPermissionChecker;
|
||||
import com.agileboot.admin.customize.service.permission.model.DataCondition;
|
||||
import com.agileboot.domain.system.dept.db.SysDeptService;
|
||||
import java.util.Objects;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 数据权限测试接口
|
||||
* @author valarchie
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class DeptTreeDataPermissionChecker extends AbstractDataPermissionChecker {
|
||||
|
||||
private SysDeptService deptService;
|
||||
|
||||
@Override
|
||||
public boolean check(SystemLoginUser loginUser, DataCondition condition) {
|
||||
if (condition == null || loginUser == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (loginUser.getDeptId() == null || condition.getTargetDeptId() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Long currentDeptId = loginUser.getDeptId();
|
||||
Long targetDeptId = condition.getTargetDeptId();
|
||||
|
||||
boolean isContainsTargetDept = deptService.isChildOfTheDept(loginUser.getDeptId(), targetDeptId);
|
||||
boolean isSameDept = Objects.equals(currentDeptId, targetDeptId);
|
||||
|
||||
return isContainsTargetDept || isSameDept;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.agileboot.admin.customize.service.permission.model.checker;
|
||||
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.admin.customize.service.permission.model.AbstractDataPermissionChecker;
|
||||
import com.agileboot.admin.customize.service.permission.model.DataCondition;
|
||||
import com.agileboot.domain.system.dept.db.SysDeptService;
|
||||
import java.util.Objects;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 数据权限测试接口
|
||||
* @author valarchie
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class OnlySelfDataPermissionChecker extends AbstractDataPermissionChecker {
|
||||
|
||||
private SysDeptService deptService;
|
||||
|
||||
@Override
|
||||
public boolean check(SystemLoginUser loginUser, DataCondition condition) {
|
||||
if (condition == null || loginUser == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (loginUser.getUserId() == null || condition.getTargetUserId() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Long currentUserId = loginUser.getUserId();
|
||||
Long targetUserId = condition.getTargetUserId();
|
||||
return Objects.equals(currentUserId, targetUserId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package com.agileboot.admin.customize.service.permission.model.checker;
|
||||
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import com.agileboot.admin.customize.service.permission.model.AbstractDataPermissionChecker;
|
||||
import com.agileboot.admin.customize.service.permission.model.DataCondition;
|
||||
import com.agileboot.domain.system.dept.db.SysDeptService;
|
||||
import java.util.Objects;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 数据权限测试接口
|
||||
* @author valarchie
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class SingleDeptDataPermissionChecker extends AbstractDataPermissionChecker {
|
||||
|
||||
private SysDeptService deptService;
|
||||
|
||||
@Override
|
||||
public boolean check(SystemLoginUser loginUser, DataCondition condition) {
|
||||
if (condition == null || loginUser == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (loginUser.getDeptId() == null || condition.getTargetDeptId() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Long currentDeptId = loginUser.getDeptId();
|
||||
Long targetDeptId = condition.getTargetDeptId();
|
||||
|
||||
return Objects.equals(currentDeptId, targetDeptId);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
# 数据源配置
|
||||
spring:
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
druid:
|
||||
webStatFilter:
|
||||
enabled: true
|
||||
statViewServlet:
|
||||
enabled: true
|
||||
# 设置白名单,不填则允许所有访问
|
||||
allow:
|
||||
url-pattern: /druid/*
|
||||
# 控制台管理用户名和密码
|
||||
login-username: agileboot
|
||||
login-password: 123456
|
||||
filter:
|
||||
stat:
|
||||
enabled: true
|
||||
# 慢SQL记录
|
||||
log-slow-sql: true
|
||||
slow-sql-millis: 1000
|
||||
merge-sql: true
|
||||
wall:
|
||||
config:
|
||||
multi-statement-allow: true
|
||||
dynamic:
|
||||
primary: master
|
||||
strict: false
|
||||
druid:
|
||||
# 初始连接数
|
||||
initialSize: 5
|
||||
# 最小连接池数量
|
||||
minIdle: 10
|
||||
# 最大连接池数量
|
||||
maxActive: 20
|
||||
# 配置获取连接等待超时的时间
|
||||
maxWait: 60000
|
||||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
|
||||
timeBetweenEvictionRunsMillis: 60000
|
||||
# 配置一个连接在池中最小生存的时间,单位是毫秒
|
||||
minEvictableIdleTimeMillis: 300000
|
||||
# 配置一个连接在池中最大生存的时间,单位是毫秒
|
||||
maxEvictableIdleTimeMillis: 900000
|
||||
# 配置检测连接是否有效
|
||||
validationQuery: SELECT 1 FROM DUAL
|
||||
testWhileIdle: true
|
||||
testOnBorrow: false
|
||||
testOnReturn: false
|
||||
datasource:
|
||||
# 主库数据源
|
||||
master:
|
||||
url: jdbc:mysql://mysql2.sqlpub.com:3307/agileboot?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&sslMode=REQUIRED
|
||||
username: ENC(s4kjpEsplGGLeV3YRNvJpJhDSOAO0tEf)
|
||||
password: ENC(hg/hxmducWsI8u83/eXgAi8yHBDFbB5z0xzwNtBejPc=)
|
||||
# 从库数据源
|
||||
# slave:
|
||||
# url: jdbc:mysql://localhost:33067/agileboot2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
# username: root
|
||||
# password: 12345
|
||||
|
||||
# redis 配置
|
||||
redis:
|
||||
host: redis
|
||||
port: 6379
|
||||
database: 1
|
||||
password: ENC(s3HU866TUAjzrWStN7kpQQ==)
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 8
|
||||
# 连接池的最大数据库连接数
|
||||
max-active: 8
|
||||
# #连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
|
||||
logging:
|
||||
file:
|
||||
path: ./logs/agileboot-dev
|
||||
|
||||
|
||||
springdoc:
|
||||
swagger-ui:
|
||||
# ***注意*** 开启Swagger UI界面 **安全考虑的话生产环境需要关掉**
|
||||
# 因为knife4j的一些配置不灵活 所以重新改回springdoc+swagger的组合 真实开发的时候 使用apifox这种工具效率更高
|
||||
enabled: true
|
||||
url: ${agileboot.api-prefix}/v3/api-docs
|
||||
config-url: ${agileboot.api-prefix}/v3/api-docs/swagger-config
|
||||
|
||||
|
||||
# 项目相关配置
|
||||
agileboot:
|
||||
# 文件基路径 示例( Windows配置D:\agileboot,Linux配置 /home/agileboot)
|
||||
file-base-dir: D:\agileboot
|
||||
# 前端url请求转发前缀
|
||||
api-prefix: /dev-api
|
||||
demo-enabled: false
|
||||
jasypt:
|
||||
encryptor:
|
||||
password: ${JASYPT_ENCRYPTOR_PASSWORD:}
|
||||
@@ -1,53 +0,0 @@
|
||||
|
||||
# 数据源配置
|
||||
spring:
|
||||
datasource:
|
||||
# 驱动
|
||||
driver-class-name: org.h2.Driver
|
||||
dynamic:
|
||||
primary: master
|
||||
strict: false
|
||||
datasource:
|
||||
master:
|
||||
# h2 内存数据库 内存模式连接配置 库名: agileboot
|
||||
url: jdbc:h2:mem:agileboot;DB_CLOSE_DELAY=-1;MODE=MySQL
|
||||
h2:
|
||||
# 开启console 访问 默认false
|
||||
console:
|
||||
enabled: true
|
||||
settings:
|
||||
# 开启h2 console 跟踪 方便调试 默认 false
|
||||
trace: true
|
||||
# 允许console 远程访问 默认false
|
||||
web-allow-others: true
|
||||
# h2 访问路径上下文
|
||||
path: /h2-console
|
||||
|
||||
sql:
|
||||
init:
|
||||
platform: mysql
|
||||
# 初始化数据
|
||||
schema-locations: classpath:h2sql/agileboot_schema.sql
|
||||
data-locations: classpath:h2sql/agileboot_data.sql
|
||||
|
||||
# redis 配置
|
||||
redis:
|
||||
# 地址
|
||||
host: localhost
|
||||
# 端口,默认为6379
|
||||
port: 36379
|
||||
# 数据库索引
|
||||
database: 0
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 8
|
||||
# 连接池的最大数据库连接数
|
||||
max-active: 8
|
||||
# #连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
# 开发环境配置
|
||||
server:
|
||||
# 服务器的HTTP端口,默认为8080
|
||||
port: 18080
|
||||
servlet:
|
||||
# 应用的访问路径
|
||||
context-path: /
|
||||
tomcat:
|
||||
# tomcat的URI编码
|
||||
uri-encoding: UTF-8
|
||||
# 连接数满后的排队数,默认为100
|
||||
accept-count: 1000
|
||||
threads:
|
||||
# tomcat最大线程数,默认为200
|
||||
max: 800
|
||||
# Tomcat启动初始化的线程数,默认值10
|
||||
min-spare: 100
|
||||
|
||||
|
||||
# Spring配置 如果需要无Mysql 无Redis直接启动的话 dev改为test
|
||||
# 生产环境把dev改为prod
|
||||
spring:
|
||||
profiles:
|
||||
active: basic,dev
|
||||
|
||||
# 如果需要无Mysql 无Redis直接启动的话 可以将这两个参数置为true, 并且spring.profile.active: dev换成test
|
||||
# redis的端口可能会被占用,如果被占用请自己修改一下端口号
|
||||
agileboot:
|
||||
embedded:
|
||||
mysql: false
|
||||
redis: false
|
||||
|
||||
|
||||
|
||||
springdoc:
|
||||
api-docs:
|
||||
enabled: true
|
||||
groups:
|
||||
enabled: true
|
||||
group-configs:
|
||||
- group: '公共API'
|
||||
packages-to-scan: com.agileboot.admin.controller.common
|
||||
- group: '内置系统API'
|
||||
packages-to-scan: com.agileboot.admin.controller.system
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>agileboot</artifactId>
|
||||
<groupId>com.agileboot</groupId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>agileboot-api</artifactId>
|
||||
|
||||
<description>
|
||||
外部API
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- 核心模块-->
|
||||
<dependency>
|
||||
<groupId>com.agileboot</groupId>
|
||||
<artifactId>agileboot-infrastructure</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!--使用undertow依赖-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-undertow</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 业务领域 -->
|
||||
<dependency>
|
||||
<groupId>com.agileboot</groupId>
|
||||
<artifactId>agileboot-domain</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.agileboot.api;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
/**
|
||||
* 启动程序 定制banner.txt的网站
|
||||
* <a href="http://patorjk.com/software/taag">http://patorjk.com/software/taag</a>
|
||||
* <a href="http://www.network-science.de/ascii/">http://www.network-science.de/ascii/</a>
|
||||
* <a href="http://www.degraeve.com/img2txt.php">http://www.degraeve.com/img2txt.php</a>
|
||||
* <a href="http://life.chacuo.net/convertfont2char">http://life.chacuo.net/convertfont2char</a>
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
|
||||
@ComponentScan(basePackages = "com.agileboot.*")
|
||||
public class AgileBooApiApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AgileBooApiApplication.class, args);
|
||||
String successMsg = " ____ _ _ __ _ _ \n"
|
||||
+ " / ___| | |_ __ _ _ __ | |_ _ _ _ __ ___ _ _ ___ ___ ___ ___ ___ / _| _ _ | || |\n"
|
||||
+ " \\___ \\ | __|/ _` || '__|| __| | | | || '_ \\ / __|| | | | / __|/ __|/ _ \\/ __|/ __|| |_ | | | || || |\n"
|
||||
+ " ___) || |_| (_| || | | |_ | |_| || |_) | \\__ \\| |_| || (__| (__| __/\\__ \\\\__ \\| _|| |_| || ||_|\n"
|
||||
+ " |____/ \\__|\\__,_||_| \\__| \\__,_|| .__/ |___/ \\__,_| \\___|\\___|\\___||___/|___/|_| \\__,_||_|(_)\n"
|
||||
+ " |_| ";
|
||||
|
||||
System.out.println(successMsg);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.agileboot.api.controller;
|
||||
|
||||
import com.agileboot.common.core.base.BaseController;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 调度日志操作处理
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/order")
|
||||
public class OrderController extends BaseController {
|
||||
|
||||
/**
|
||||
* 访问首页,提示语
|
||||
*/
|
||||
@RequestMapping("/")
|
||||
public String index() {
|
||||
return "暂无订单";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.agileboot.api.controller.app;
|
||||
|
||||
import com.agileboot.api.customize.service.JwtTokenService;
|
||||
import com.agileboot.common.core.base.BaseController;
|
||||
import com.agileboot.common.core.dto.ResponseDTO;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 调度日志操作处理
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/app")
|
||||
@AllArgsConstructor
|
||||
public class AppController extends BaseController {
|
||||
|
||||
private final JwtTokenService jwtTokenService;
|
||||
|
||||
/**
|
||||
* 访问首页,提示语
|
||||
*/
|
||||
@PreAuthorize("hasAuthority('annie')")
|
||||
@GetMapping("/list")
|
||||
public ResponseDTO<?> appLogin() {
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package com.agileboot.api.controller.common;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import com.agileboot.api.customize.service.JwtTokenService;
|
||||
import com.agileboot.common.core.base.BaseController;
|
||||
import com.agileboot.common.core.dto.ResponseDTO;
|
||||
import java.util.Map;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 调度日志操作处理
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/common")
|
||||
@AllArgsConstructor
|
||||
public class LoginController extends BaseController {
|
||||
|
||||
private final JwtTokenService jwtTokenService;
|
||||
|
||||
/**
|
||||
* 访问首页,提示语
|
||||
*/
|
||||
@PostMapping("/app/{appId}/login")
|
||||
public ResponseDTO<String> appLogin() {
|
||||
String token = jwtTokenService.generateToken(MapUtil.of("token", "user1"));
|
||||
return ResponseDTO.ok(token);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package com.agileboot.api.customize.config;
|
||||
|
||||
import com.agileboot.api.customize.service.JwtTokenService;
|
||||
import com.agileboot.infrastructure.user.app.AppLoginUser;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import java.io.IOException;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
/**
|
||||
* token过滤器 验证token有效性
|
||||
* 继承OncePerRequestFilter类的话 可以确保只执行filter一次, 避免执行多次
|
||||
* @author valarchie
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
@Autowired
|
||||
private JwtTokenService jwtTokenService;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
String tokenFromRequest = jwtTokenService.getTokenFromRequest(request);
|
||||
|
||||
if (tokenFromRequest != null) {
|
||||
Claims claims = jwtTokenService.parseToken(tokenFromRequest);
|
||||
String token = (String) claims.get("token");
|
||||
// 根据token去查缓存里面 有没有对应的App用户
|
||||
// 没有的话 再去数据库中查询
|
||||
if (token != null && token.equals("user1")) {
|
||||
AppLoginUser loginUser = new AppLoginUser(23232323L, false, "dasdsadsds");
|
||||
loginUser.grantAppPermission("annie");
|
||||
UsernamePasswordAuthenticationToken suer1 = new UsernamePasswordAuthenticationToken(loginUser, null,
|
||||
loginUser.getAuthorities());
|
||||
SecurityContextHolder.getContext().setAuthentication(suer1);
|
||||
}
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package com.agileboot.api.customize.config;
|
||||
|
||||
import com.agileboot.common.core.dto.ResponseDTO;
|
||||
import com.agileboot.common.exception.ApiException;
|
||||
import com.agileboot.common.exception.error.ErrorCode.Client;
|
||||
import com.agileboot.common.utils.ServletHolderUtil;
|
||||
import com.agileboot.common.utils.jackson.JacksonUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
/**
|
||||
* 主要配置登录流程逻辑涉及以下几个类
|
||||
|
||||
* @author valarchie
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
@RequiredArgsConstructor
|
||||
public class SecurityConfig {
|
||||
/**
|
||||
* token认证过滤器
|
||||
*/
|
||||
private final JwtAuthenticationFilter jwtTokenFilter;
|
||||
|
||||
|
||||
/**
|
||||
* 跨域过滤器
|
||||
*/
|
||||
private final CorsFilter corsFilter;
|
||||
|
||||
|
||||
/**
|
||||
* 登录异常处理类
|
||||
* 用户未登陆的话 在这个Bean中处理
|
||||
*/
|
||||
@Bean
|
||||
public AuthenticationEntryPoint customAuthenticationEntryPoint() {
|
||||
return (request, response, exception) -> {
|
||||
ResponseDTO<Void> responseDTO = ResponseDTO.fail(
|
||||
new ApiException(Client.COMMON_NO_AUTHORIZATION, request.getRequestURI())
|
||||
);
|
||||
ServletHolderUtil.renderString(response, JacksonUtil.to(responseDTO));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
|
||||
httpSecurity.csrf().disable()
|
||||
// 不配这个错误处理的话 会直接返回403
|
||||
.exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint())
|
||||
.and()
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 禁用 session
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.antMatchers("/common/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
// 禁用 X-Frame-Options 响应头。下面是具体解释:
|
||||
// X-Frame-Options 是一个 HTTP 响应头,用于防止网页被嵌入到其他网页的 <frame>、<iframe> 或 <object> 标签中,从而可以减少点击劫持攻击的风险
|
||||
.headers().frameOptions().disable()
|
||||
.and()
|
||||
.formLogin().disable();
|
||||
|
||||
httpSecurity.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
// 添加CORS filter
|
||||
httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationFilter.class);
|
||||
|
||||
|
||||
return httpSecurity.build();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
package com.agileboot.api.customize.service;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.agileboot.common.constant.Constants.Token;
|
||||
import com.agileboot.common.exception.ApiException;
|
||||
import com.agileboot.common.exception.error.ErrorCode;
|
||||
import com.agileboot.domain.common.cache.RedisCacheService;
|
||||
import com.agileboot.infrastructure.user.web.SystemLoginUser;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.MalformedJwtException;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.SignatureException;
|
||||
import io.jsonwebtoken.UnsupportedJwtException;
|
||||
import java.util.Map;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* token验证处理
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
@Data
|
||||
@RequiredArgsConstructor
|
||||
public class JwtTokenService {
|
||||
|
||||
/**
|
||||
* 自定义令牌标识
|
||||
*/
|
||||
@Value("${token.header}")
|
||||
private String header;
|
||||
|
||||
/**
|
||||
* 令牌秘钥
|
||||
*/
|
||||
@Value("${token.secret}")
|
||||
private String secret;
|
||||
|
||||
private final RedisCacheService redisCache;
|
||||
|
||||
/**
|
||||
* 获取用户身份信息
|
||||
*
|
||||
* @return 用户信息
|
||||
*/
|
||||
public SystemLoginUser getLoginUser(HttpServletRequest request) {
|
||||
// 获取请求携带的令牌
|
||||
String token = getTokenFromRequest(request);
|
||||
if (StrUtil.isNotEmpty(token)) {
|
||||
try {
|
||||
Claims claims = parseToken(token);
|
||||
// 解析对应的权限以及用户信息
|
||||
String uuid = (String) claims.get(Token.LOGIN_USER_KEY);
|
||||
|
||||
return redisCache.loginUserCache.getObjectOnlyInCacheById(uuid);
|
||||
} catch (SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException jwtException) {
|
||||
log.error("parse token failed.", jwtException);
|
||||
throw new ApiException(jwtException, ErrorCode.Client.INVALID_TOKEN);
|
||||
} catch (Exception e) {
|
||||
log.error("fail to get cached user from redis", e);
|
||||
throw new ApiException(e, ErrorCode.Client.TOKEN_PROCESS_FAILED, e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 从数据声明生成令牌
|
||||
*
|
||||
* @param claims 数据声明
|
||||
* @return 令牌
|
||||
*/
|
||||
public String generateToken(Map<String, Object> claims) {
|
||||
return Jwts.builder()
|
||||
.setClaims(claims)
|
||||
.signWith(SignatureAlgorithm.HS512, secret).compact();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从令牌中获取数据声明
|
||||
*
|
||||
* @param token 令牌
|
||||
* @return 数据声明
|
||||
*/
|
||||
public Claims parseToken(String token) {
|
||||
return Jwts.parser()
|
||||
.setSigningKey(secret)
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从令牌中获取用户名
|
||||
*
|
||||
* @param token 令牌
|
||||
* @return 用户名
|
||||
*/
|
||||
private String getUsernameFromToken(String token) {
|
||||
Claims claims = parseToken(token);
|
||||
return claims.getSubject();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求token
|
||||
*
|
||||
* @return token
|
||||
*/
|
||||
public String getTokenFromRequest(HttpServletRequest request) {
|
||||
String token = request.getHeader(header);
|
||||
if (StrUtil.isNotEmpty(token) && token.startsWith(Token.PREFIX)) {
|
||||
token = StrUtil.stripIgnoreCase(token, Token.PREFIX, null);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.agileboot.api.customize.util;
|
||||
|
||||
public class ApiEncryptor {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
# 开发环境配置
|
||||
server:
|
||||
# 服务器的HTTP端口,默认为8080
|
||||
port: 8090
|
||||
servlet:
|
||||
# 应用的访问路径
|
||||
context-path: /
|
||||
tomcat:
|
||||
# tomcat的URI编码
|
||||
uri-encoding: UTF-8
|
||||
# 连接数满后的排队数,默认为100
|
||||
accept-count: 1000
|
||||
threads:
|
||||
# tomcat最大线程数,默认为200
|
||||
max: 800
|
||||
# Tomcat启动初始化的线程数,默认值10
|
||||
min-spare: 100
|
||||
|
||||
|
||||
# Spring配置
|
||||
spring:
|
||||
profiles:
|
||||
active: basic,dev
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.agileboot</groupId>
|
||||
<artifactId>agileboot</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>agileboot-boot-start</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.agileboot</groupId>
|
||||
<artifactId>wol-common-web</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.agileboot</groupId>
|
||||
<artifactId>wol-module-ai</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,4 +0,0 @@
|
||||
server:
|
||||
port: 8080
|
||||
servlet:
|
||||
context-path: /api
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.agileboot;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* @Author cuiJiaWang
|
||||
* @Create 2025-08-12 17:35
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class AgilebootCloudApplication {
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.agileboot</groupId>
|
||||
<artifactId>agileboot</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>agileboot-cloud</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>agileboot-cloud-start</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
@@ -17,6 +17,8 @@
|
||||
<module>wol-common-mybatis</module>
|
||||
<module>wol-common-redis</module>
|
||||
<module>wol-common-json</module>
|
||||
<module>wol-common-satoken</module>
|
||||
<module>wol-common-nacos</module>
|
||||
</modules>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
|
||||
@@ -47,7 +47,16 @@
|
||||
<artifactId>wol-common-json</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.agileboot</groupId>
|
||||
<artifactId>wol-common-satoken</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.agileboot</groupId>
|
||||
<artifactId>wol-common-nacos</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
@@ -85,7 +85,16 @@
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
</dependency>
|
||||
<!--ENC加密-->
|
||||
<dependency>
|
||||
<groupId>com.github.ulisesbocchio</groupId>
|
||||
<artifactId>jasypt-spring-boot-starter</artifactId>
|
||||
<version>2.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- swagger注解 -->
|
||||
<dependency>
|
||||
|
||||
@@ -45,11 +45,13 @@ public interface Constants {
|
||||
* 通用成功标识
|
||||
*/
|
||||
String SUCCESS = "0";
|
||||
String NORMAL = "0"; // 正常状态
|
||||
|
||||
/**
|
||||
* 通用失败标识
|
||||
*/
|
||||
String FAIL = "1";
|
||||
String DISABLE = "1"; // 异常/停用状态
|
||||
|
||||
/**
|
||||
* 登录成功
|
||||
@@ -80,6 +82,45 @@ public interface Constants {
|
||||
* 顶级部门id
|
||||
*/
|
||||
Long TOP_PARENT_ID = 0L;
|
||||
/**
|
||||
* 超级管理员ID
|
||||
*/
|
||||
Long SUPER_ADMIN_ID = 1L;
|
||||
/**
|
||||
* 超级管理员角色 roleKey
|
||||
*/
|
||||
String SUPER_ADMIN_ROLE_KEY = "superadmin";
|
||||
|
||||
/**
|
||||
* 租户管理员角色 roleKey
|
||||
*/
|
||||
String TENANT_ADMIN_ROLE_KEY = "admin";
|
||||
|
||||
/**
|
||||
* 租户管理员角色名称
|
||||
*/
|
||||
String TENANT_ADMIN_ROLE_NAME = "管理员";
|
||||
|
||||
/**
|
||||
* 默认租户ID
|
||||
*/
|
||||
String DEFAULT_TENANT_ID = "000000";
|
||||
|
||||
interface Cache {
|
||||
/**
|
||||
* 全局 redis key (业务无关的key)
|
||||
*/
|
||||
String GLOBAL_REDIS_KEY = "global:";
|
||||
|
||||
/**
|
||||
* 登录账户密码错误次数 redis key
|
||||
*/
|
||||
String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
|
||||
/**
|
||||
* 验证码 redis key
|
||||
*/
|
||||
String CAPTCHA_CODE_KEY = GLOBAL_REDIS_KEY + "captcha_codes:";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ public interface BasicEnum<T>{
|
||||
* 获取枚举的描述
|
||||
* @return 描述
|
||||
*/
|
||||
String description();
|
||||
String getDesc();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.agileboot.common.core.enums;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.agileboot.common.core.exception.ApiException;
|
||||
import com.agileboot.common.core.exception.error.ErrorCode;
|
||||
import com.agileboot.common.core.enums.BasicEnum;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -55,7 +54,7 @@ public class BasicEnumUtil {
|
||||
public static <E extends Enum<E>> String getDescriptionByValue(Class<E> enumClass, Object value) {
|
||||
E basicEnum = fromValueSafely(enumClass, value);
|
||||
if (basicEnum != null) {
|
||||
return ((BasicEnum<?>) basicEnum).description();
|
||||
return ((BasicEnum<?>) basicEnum).getDesc();
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,6 @@ public interface DictionaryEnum<T> extends BasicEnum<T> {
|
||||
* 获取css标签
|
||||
* @return css标签
|
||||
*/
|
||||
String cssTag();
|
||||
String getCssTag();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.agileboot.common.core.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 登录类型
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum LoginType {
|
||||
|
||||
/**
|
||||
* 密码登录
|
||||
*/
|
||||
PASSWORD("user.password.retry.limit.exceed", "user.password.retry.limit.count"),
|
||||
|
||||
/**
|
||||
* 短信登录
|
||||
*/
|
||||
SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"),
|
||||
|
||||
/**
|
||||
* 邮箱登录
|
||||
*/
|
||||
EMAIL("email.code.retry.limit.exceed", "email.code.retry.limit.count"),
|
||||
|
||||
/**
|
||||
* 小程序登录
|
||||
*/
|
||||
XCX("", "");
|
||||
|
||||
/**
|
||||
* 登录重试超出限制提示
|
||||
*/
|
||||
final String retryLimitExceed;
|
||||
|
||||
/**
|
||||
* 登录重试限制计数提示
|
||||
*/
|
||||
final String retryLimitCount;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.agileboot.common.core.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* 用户类型
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum UserType {
|
||||
|
||||
/**
|
||||
* 后台系统用户
|
||||
*/
|
||||
SYS_USER("sys_user"),
|
||||
|
||||
/**
|
||||
* 移动客户端用户
|
||||
*/
|
||||
APP_USER("app_user");
|
||||
|
||||
/**
|
||||
* 用户类型标识(用于 token、权限识别等)
|
||||
*/
|
||||
private final String userType;
|
||||
|
||||
public static UserType getUserType(String str) {
|
||||
for (UserType value : values()) {
|
||||
if (StringUtils.contains(str, value.getUserType())) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("'UserType' not found By " + str);
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,16 @@ package com.agileboot.common.core.enums.common;
|
||||
import com.agileboot.common.core.enums.dictionary.CssTag;
|
||||
import com.agileboot.common.core.enums.dictionary.Dictionary;
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 对应sys_operation_log的business_type
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@Dictionary(name = "sysOperationLog.businessType")
|
||||
public enum BusinessTypeEnum implements DictionaryEnum<Integer> {
|
||||
|
||||
@@ -26,29 +30,7 @@ public enum BusinessTypeEnum implements DictionaryEnum<Integer> {
|
||||
CLEAN(8, "清空", CssTag.DANGER),
|
||||
;
|
||||
|
||||
private final int value;
|
||||
private final String description;
|
||||
private final Integer value;
|
||||
private final String desc;
|
||||
private final String cssTag;
|
||||
|
||||
BusinessTypeEnum(int value, String description, String cssTag) {
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
this.cssTag = cssTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cssTag() {
|
||||
return cssTag;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
package com.agileboot.common.core.enums.common;
|
||||
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import com.agileboot.common.core.enums.dictionary.CssTag;
|
||||
import com.agileboot.common.core.enums.dictionary.Dictionary;
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 对应sys_user的sex字段
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@Dictionary(name = "sysUser.sex")
|
||||
public enum GenderEnum implements DictionaryEnum<Integer> {
|
||||
|
||||
@@ -19,29 +23,7 @@ public enum GenderEnum implements DictionaryEnum<Integer> {
|
||||
FEMALE(2, "女", CssTag.PRIMARY),
|
||||
UNKNOWN(0, "未知", CssTag.PRIMARY);
|
||||
|
||||
private final int value;
|
||||
private final String description;
|
||||
private final Integer value;
|
||||
private final String desc;
|
||||
private final String cssTag;
|
||||
|
||||
GenderEnum(int value, String description, String cssTag) {
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
this.cssTag = cssTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cssTag() {
|
||||
return cssTag;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
package com.agileboot.common.core.enums.common;
|
||||
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import com.agileboot.common.core.enums.dictionary.CssTag;
|
||||
import com.agileboot.common.core.enums.dictionary.Dictionary;
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 用户状态
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
// TODO 表记得改成LoginLog
|
||||
@Dictionary(name = "sysLoginLog.status")
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum LoginStatusEnum implements DictionaryEnum<Integer> {
|
||||
/**
|
||||
* status of user
|
||||
@@ -19,28 +24,7 @@ public enum LoginStatusEnum implements DictionaryEnum<Integer> {
|
||||
REGISTER(3, "注册", CssTag.PRIMARY),
|
||||
LOGIN_FAIL(0, "登录失败", CssTag.DANGER);
|
||||
|
||||
private final int value;
|
||||
private final String msg;
|
||||
private final Integer value;
|
||||
private final String desc;
|
||||
private final String cssTag;
|
||||
|
||||
LoginStatusEnum(int status, String msg, String cssTag) {
|
||||
this.value = status;
|
||||
this.msg = msg;
|
||||
this.cssTag = cssTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cssTag() {
|
||||
return cssTag;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,25 @@
|
||||
package com.agileboot.common.core.enums.common;
|
||||
|
||||
import com.agileboot.common.core.enums.BasicEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Deprecated
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum MenuComponentEnum implements BasicEnum<Integer> {
|
||||
|
||||
/**
|
||||
* 菜单组件类型
|
||||
*/
|
||||
LAYOUT(1,"Layout"),
|
||||
PARENT_VIEW(2,"ParentView"),
|
||||
INNER_LINK(3,"InnerLink");
|
||||
LAYOUT(1, "Layout"),
|
||||
PARENT_VIEW(2, "ParentView"),
|
||||
INNER_LINK(3, "InnerLink");
|
||||
|
||||
private final int value;
|
||||
private final String description;
|
||||
private final Integer value;
|
||||
private final String desc;
|
||||
|
||||
MenuComponentEnum(int value, String description) {
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package com.agileboot.common.core.enums.common;
|
||||
|
||||
import com.agileboot.common.core.enums.BasicEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author valarchie
|
||||
* 对应 sys_menu表的menu_type字段
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum MenuTypeEnum implements BasicEnum<Integer> {
|
||||
|
||||
/**
|
||||
@@ -16,23 +20,7 @@ public enum MenuTypeEnum implements BasicEnum<Integer> {
|
||||
IFRAME(3, "内嵌Iframe"),
|
||||
OUTSIDE_LINK_REDIRECT(4, "外链跳转");
|
||||
|
||||
private final int value;
|
||||
private final String description;
|
||||
|
||||
MenuTypeEnum(int value, String description) {
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
private final Integer value;
|
||||
private final String desc;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
package com.agileboot.common.core.enums.common;
|
||||
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import com.agileboot.common.core.enums.dictionary.CssTag;
|
||||
import com.agileboot.common.core.enums.dictionary.Dictionary;
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 对应sys_notice的 status字段
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Dictionary(name = "sysNotice.status")
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum NoticeStatusEnum implements DictionaryEnum<Integer> {
|
||||
|
||||
/**
|
||||
@@ -17,29 +22,8 @@ public enum NoticeStatusEnum implements DictionaryEnum<Integer> {
|
||||
OPEN(1, "正常", CssTag.PRIMARY),
|
||||
CLOSE(0, "关闭", CssTag.DANGER);
|
||||
|
||||
private final int value;
|
||||
private final String description;
|
||||
private final Integer value;
|
||||
private final String desc;
|
||||
private final String cssTag;
|
||||
|
||||
NoticeStatusEnum(int value, String description, String cssTag) {
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
this.cssTag = cssTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cssTag() {
|
||||
return cssTag;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
package com.agileboot.common.core.enums.common;
|
||||
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import com.agileboot.common.core.enums.dictionary.CssTag;
|
||||
import com.agileboot.common.core.enums.dictionary.Dictionary;
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 对应sys_notice的 notice_type字段
|
||||
* 名称一般由对应的表名.字段构成
|
||||
* 全局的话使用common作为表名
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Dictionary(name = "sysNotice.noticeType")
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum NoticeTypeEnum implements DictionaryEnum<Integer> {
|
||||
|
||||
/**
|
||||
@@ -19,29 +24,16 @@ public enum NoticeTypeEnum implements DictionaryEnum<Integer> {
|
||||
NOTIFICATION(1, "通知", CssTag.WARNING),
|
||||
ANNOUNCEMENT(2, "公告", CssTag.SUCCESS);
|
||||
|
||||
private final int value;
|
||||
private final String description;
|
||||
private final Integer value;
|
||||
private final String desc;
|
||||
private final String cssTag;
|
||||
|
||||
NoticeTypeEnum(int value, String description, String cssTag) {
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
this.cssTag = cssTag;
|
||||
public static Integer getDescByValue(Integer value) {
|
||||
for (NoticeTypeEnum item : values()) {
|
||||
if (item.value.equals(value)) {
|
||||
return item.value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cssTag() {
|
||||
return cssTag;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
package com.agileboot.common.core.enums.common;
|
||||
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import com.agileboot.common.core.enums.dictionary.CssTag;
|
||||
import com.agileboot.common.core.enums.dictionary.Dictionary;
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 对应sys_operation_log的status字段
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Dictionary(name = "sysOperationLog.status")
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum OperationStatusEnum implements DictionaryEnum<Integer> {
|
||||
|
||||
/**
|
||||
@@ -17,29 +22,8 @@ public enum OperationStatusEnum implements DictionaryEnum<Integer> {
|
||||
SUCCESS(1, "成功", CssTag.PRIMARY),
|
||||
FAIL(0, "失败", CssTag.DANGER);
|
||||
|
||||
private final int value;
|
||||
private final String description;
|
||||
private final Integer value;
|
||||
private final String desc;
|
||||
private final String cssTag;
|
||||
|
||||
OperationStatusEnum(int value, String description, String cssTag) {
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
this.cssTag = cssTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cssTag() {
|
||||
return cssTag;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package com.agileboot.common.core.enums.common;
|
||||
|
||||
import com.agileboot.common.core.enums.dictionary.Dictionary;
|
||||
import com.agileboot.common.core.enums.BasicEnum;
|
||||
import com.agileboot.common.core.enums.dictionary.Dictionary;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 操作者类型
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@Dictionary(name = "sysOperationLog.operatorType")
|
||||
public enum OperatorTypeEnum implements BasicEnum<Integer> {
|
||||
|
||||
@@ -17,23 +22,8 @@ public enum OperatorTypeEnum implements BasicEnum<Integer> {
|
||||
WEB(2, "Web用户"),
|
||||
MOBILE(3, "手机端用户");
|
||||
|
||||
private final int value;
|
||||
private final String description;
|
||||
|
||||
OperatorTypeEnum(int value, String description) {
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
private final Integer value;
|
||||
private final String desc;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
package com.agileboot.common.core.enums.common;
|
||||
|
||||
import com.agileboot.common.core.enums.BasicEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Http Method
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum RequestMethodEnum implements BasicEnum<Integer> {
|
||||
|
||||
/**
|
||||
@@ -17,23 +22,8 @@ public enum RequestMethodEnum implements BasicEnum<Integer> {
|
||||
DELETE(4, "DELETE"),
|
||||
UNKNOWN(-1, "UNKNOWN");
|
||||
|
||||
private final int value;
|
||||
private final String description;
|
||||
|
||||
RequestMethodEnum(int value, String description) {
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
private final Integer value;
|
||||
private final String desc;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
package com.agileboot.common.core.enums.common;
|
||||
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import com.agileboot.common.core.enums.dictionary.CssTag;
|
||||
import com.agileboot.common.core.enums.dictionary.Dictionary;
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 除非表有特殊指明的话,一般用这个枚举代表 status字段
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Dictionary(name = "common.status")
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum StatusEnum implements DictionaryEnum<Integer> {
|
||||
/**
|
||||
* 开关状态
|
||||
@@ -16,29 +21,17 @@ public enum StatusEnum implements DictionaryEnum<Integer> {
|
||||
ENABLE(1, "正常", CssTag.PRIMARY),
|
||||
DISABLE(0, "停用", CssTag.DANGER);
|
||||
|
||||
private final int value;
|
||||
private final String description;
|
||||
private final Integer value;
|
||||
private final String desc;
|
||||
private final String cssTag;
|
||||
|
||||
StatusEnum(int value, String description, String cssTag) {
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
this.cssTag = cssTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cssTag() {
|
||||
return cssTag;
|
||||
public static Integer getDescByValue(Integer value) {
|
||||
for (StatusEnum item : StatusEnum.values()) {
|
||||
if (item.value.equals(value)) {
|
||||
return item.value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
package com.agileboot.common.core.enums.common;
|
||||
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import com.agileboot.common.core.enums.dictionary.CssTag;
|
||||
import com.agileboot.common.core.enums.dictionary.Dictionary;
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 对应sys_user的status字段
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Dictionary(name = "sysUser.status")
|
||||
public enum UserStatusEnum implements DictionaryEnum<Integer> {
|
||||
|
||||
@@ -18,32 +23,7 @@ public enum UserStatusEnum implements DictionaryEnum<Integer> {
|
||||
DISABLED(2, "禁用", CssTag.DANGER),
|
||||
FROZEN(3, "冻结", CssTag.WARNING);
|
||||
|
||||
private final int value;
|
||||
private final String description;
|
||||
|
||||
private final Integer value;
|
||||
private final String desc;
|
||||
private final String cssTag;
|
||||
|
||||
UserStatusEnum(int value, String description, String cssTag) {
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
this.cssTag = cssTag;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cssTag() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
package com.agileboot.common.core.enums.common;
|
||||
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import com.agileboot.common.core.enums.dictionary.CssTag;
|
||||
import com.agileboot.common.core.enums.dictionary.Dictionary;
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 对应sys_menu表的is_visible字段
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Deprecated
|
||||
@Dictionary(name = "sysMenu.isVisible")
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum VisibleStatusEnum implements DictionaryEnum<Integer> {
|
||||
|
||||
/**
|
||||
@@ -18,29 +23,8 @@ public enum VisibleStatusEnum implements DictionaryEnum<Integer> {
|
||||
SHOW(1, "显示", CssTag.PRIMARY),
|
||||
HIDE(0, "隐藏", CssTag.DANGER);
|
||||
|
||||
private final int value;
|
||||
private final String description;
|
||||
private final Integer value;
|
||||
private final String desc;
|
||||
private final String cssTag;
|
||||
|
||||
VisibleStatusEnum(int value, String description, String cssTag) {
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
this.cssTag = cssTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String cssTag() {
|
||||
return cssTag;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,12 +3,17 @@ package com.agileboot.common.core.enums.common;
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import com.agileboot.common.core.enums.dictionary.CssTag;
|
||||
import com.agileboot.common.core.enums.dictionary.Dictionary;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 系统内代表是与否的枚举
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Dictionary(name = "common.yesOrNo")
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum YesOrNoEnum implements DictionaryEnum<Integer> {
|
||||
/**
|
||||
* 是与否
|
||||
@@ -16,31 +21,8 @@ public enum YesOrNoEnum implements DictionaryEnum<Integer> {
|
||||
YES(1, "是", CssTag.PRIMARY),
|
||||
NO(0, "否", CssTag.DANGER);
|
||||
|
||||
private final int value;
|
||||
private final String description;
|
||||
private final Integer value;
|
||||
private final String desc;
|
||||
private final String cssTag;
|
||||
|
||||
YesOrNoEnum(int value, String description, String cssTag) {
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
this.cssTag = cssTag;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String cssTag() {
|
||||
return cssTag;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
package com.agileboot.domain.common.cache;
|
||||
package com.agileboot.common.core.enums.dictionary;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import com.agileboot.common.enums.common.BusinessTypeEnum;
|
||||
import com.agileboot.common.enums.common.GenderEnum;
|
||||
import com.agileboot.common.enums.common.LoginStatusEnum;
|
||||
import com.agileboot.common.enums.common.NoticeStatusEnum;
|
||||
import com.agileboot.common.enums.common.NoticeTypeEnum;
|
||||
import com.agileboot.common.enums.common.OperationStatusEnum;
|
||||
import com.agileboot.common.enums.common.StatusEnum;
|
||||
import com.agileboot.common.enums.common.UserStatusEnum;
|
||||
import com.agileboot.common.enums.common.VisibleStatusEnum;
|
||||
import com.agileboot.common.enums.common.YesOrNoEnum;
|
||||
import com.agileboot.common.enums.dictionary.Dictionary;
|
||||
import com.agileboot.common.enums.DictionaryEnum;
|
||||
import com.agileboot.common.enums.dictionary.DictionaryData;
|
||||
import com.agileboot.common.core.enums.DictionaryEnum;
|
||||
import com.agileboot.common.core.enums.common.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -28,11 +17,11 @@ import java.util.stream.Collectors;
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
public class MapCache {
|
||||
public class DictCache {
|
||||
|
||||
private static final Map<String, List<DictionaryData>> DICTIONARY_CACHE = MapUtil.newHashMap(128);
|
||||
|
||||
private MapCache() {
|
||||
private DictCache() {
|
||||
}
|
||||
|
||||
static {
|
||||
@@ -17,9 +17,9 @@ public class DictionaryData {
|
||||
@SuppressWarnings("rawtypes")
|
||||
public DictionaryData(DictionaryEnum enumType) {
|
||||
if (enumType != null) {
|
||||
this.label = enumType.description();
|
||||
this.label = enumType.getDesc();
|
||||
this.value = (Integer) enumType.getValue();
|
||||
this.cssTag = enumType.cssTag();
|
||||
this.cssTag = enumType.getCssTag();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.agileboot.common.core.exception;
|
||||
|
||||
import cn.hutool.core.text.StrFormatter;
|
||||
import com.agileboot.common.core.exception.error.ErrorCodeInterface;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -34,6 +35,11 @@ public class BizException extends RuntimeException {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public BizException(ErrorCodeInterface errorCode) {
|
||||
this.code = errorCode.code();
|
||||
this.message = errorCode.message();
|
||||
}
|
||||
|
||||
public BizException(String message, Integer code) {
|
||||
this.message = message;
|
||||
this.code = code;
|
||||
@@ -42,4 +48,8 @@ public class BizException extends RuntimeException {
|
||||
public BizException(String message, Object... args) {
|
||||
this.message = StrFormatter.format(message, args);
|
||||
}
|
||||
|
||||
public BizException(ErrorCodeInterface errorCode, Object... args) {
|
||||
this.message = StrFormatter.format(errorCode.message(), args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ public enum ErrorCode implements ErrorCodeInterface {
|
||||
|
||||
ROLE_DATA_SCOPE_DUPLICATED_DEPT(11003, "重复的部门id", "Business.ROLE_DATA_SCOPE_DUPLICATED_DEPT"),
|
||||
|
||||
ROLE_ALREADY_ASSIGN_TO_USER(11004, "角色已分配给用户,请先取消分配,再删除角色", "Business.ROLE_ALREADY_ASSIGN_TO_USER"),
|
||||
ROLE_ALREADY_ASSIGN_TO_USER(11004, "角色:{} 已分配给用户,请先取消分配,再删除角色", "Business.ROLE_ALREADY_ASSIGN_TO_USER"),
|
||||
|
||||
ROLE_IS_NOT_AVAILABLE(11005, "角色:{} 已禁用,无法分配给用户", "Business.ROLE_IS_NOT_AVAILABLE"),
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.agileboot.common.core.utils;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import jakarta.validation.Validator;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Validator 校验框架工具
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class ValidatorUtils {
|
||||
|
||||
private static final Validator VALID = SpringUtil.getBean(Validator.class);
|
||||
|
||||
/**
|
||||
* 对给定对象进行参数校验,并根据指定的校验组进行校验
|
||||
*
|
||||
* @param object 要进行校验的对象
|
||||
* @param groups 校验组
|
||||
* @throws ConstraintViolationException 如果校验不通过,则抛出参数校验异常
|
||||
*/
|
||||
public static <T> void validate(T object, Class<?>... groups) {
|
||||
Set<ConstraintViolation<T>> validate = VALID.validate(object, groups);
|
||||
if (!validate.isEmpty()) {
|
||||
throw new ConstraintViolationException("参数校验异常", validate);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
27
agileboot-common/wol-common-core/src/main/resources/base.yml
Normal file
27
agileboot-common/wol-common-core/src/main/resources/base.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
wol:
|
||||
mysql:
|
||||
maser:
|
||||
url: ${MYSQL_URL:121.41.64.98:3306}
|
||||
username: ${MYSQL_USERNAME:agileboot}
|
||||
password: ${MYSQL_PASSWORD:123456}
|
||||
redis:
|
||||
database: ${REDIS_DATABASE:3}
|
||||
host: ${REDIS_HOST:121.41.64.98}
|
||||
port: ${REDIS_PORT:6379}
|
||||
password: ${REDIS_PASSWORD:'Wyy123123'}
|
||||
nacos:
|
||||
server:
|
||||
addr: ${NACOS_ADDR:192.168.110.27:8848}
|
||||
username: ${NACOS_USERNAME:nacos}
|
||||
password: ${NACOS_PASSWORD:nacos}
|
||||
namespace: ${NACOS_NAMESPACE:public}
|
||||
group: ${NACOS_GROUP:DEFAULT_GROUP}
|
||||
satoken:
|
||||
tokenName: ${SATOKEN_TOKENNAME:Authorization}
|
||||
isConcurrent: ${SATOKEN_ISCONCURRENT:true}
|
||||
isShare: ${SATOKEN_ISSHARE:true}
|
||||
isLog: ${SATOKEN_ISLOG:true}
|
||||
jwtSecretKey: ${SATOKEN_JWTSECRETKEY:abcdefghijklmnopqrstuvwxyz}
|
||||
jasypt:
|
||||
encryptor:
|
||||
password: ${JASYPT_ENCRYPTOR_PASSWORD:}
|
||||
@@ -20,6 +20,29 @@
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
</dependency>
|
||||
<!-- 分页插件 -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-jsqlparser</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
|
||||
<version>${dynamic-ds.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
<version>1.2.20</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.agileboot</groupId>
|
||||
<artifactId>wol-common-satoken</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
package com.agileboot.common.mybatis.config;
|
||||
|
||||
import com.agileboot.common.core.factory.YmlPropertySourceFactory;
|
||||
import com.agileboot.common.mybatis.handler.InjectionMetaObjectHandler;
|
||||
import com.agileboot.common.mybatis.mapper.CustomSqlInjector;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -16,9 +21,28 @@ public class MybatisPlusConfiguration {
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
// 分页插件 自动识别数据库类型
|
||||
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
|
||||
paginationInnerInterceptor.setOverflow(true); // 超出总页数后回到首页
|
||||
interceptor.addInnerInterceptor(paginationInnerInterceptor);
|
||||
// 乐观锁插件
|
||||
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
|
||||
// 阻止恶意的全表更新删除
|
||||
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 元对象字段填充控制器
|
||||
*/
|
||||
@Bean
|
||||
public MetaObjectHandler metaObjectHandler() {
|
||||
return new InjectionMetaObjectHandler();
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public CustomSqlInjector customSqlInjector() {
|
||||
return new CustomSqlInjector();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.agileboot.common.mybatis.core.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
@@ -33,8 +34,8 @@ public class BaseEntity implements Serializable {
|
||||
/**
|
||||
* 创建部门
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Long createDept;
|
||||
// @TableField(fill = FieldFill.INSERT)
|
||||
// private Long createDept;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
@@ -51,15 +52,19 @@ public class BaseEntity implements Serializable {
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
private Long updateBy;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
private Date updateTime;
|
||||
|
||||
@TableLogic
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Integer deleted;
|
||||
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
package com.agileboot.common.mybatis.core.page;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.agileboot.common.core.exception.ServiceException;
|
||||
import com.agileboot.common.core.utils.sql.SqlUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.Serial;
|
||||
@@ -24,45 +26,42 @@ import java.util.List;
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class PageQuery implements Serializable {
|
||||
public abstract class PageQuery<T> implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final int MAX_PAGE_NUM = 200; // 最大分页页数
|
||||
public static final int MAX_PAGE_SIZE = 500; // 单页最大大小
|
||||
public static final int DEFAULT_PAGE_NUM = 1; // 默认分页页数
|
||||
public static final int DEFAULT_PAGE_SIZE = 10; // 默认分页大小
|
||||
|
||||
/**
|
||||
* 分页大小
|
||||
*/
|
||||
private Integer pageSize;
|
||||
@Max(MAX_PAGE_SIZE)
|
||||
protected Integer pageSize;
|
||||
|
||||
/**
|
||||
* 当前页数
|
||||
*/
|
||||
private Integer pageNum;
|
||||
@Max(MAX_PAGE_NUM)
|
||||
protected Integer pageNum;
|
||||
|
||||
/**
|
||||
* 排序列
|
||||
*/
|
||||
private String orderByColumn;
|
||||
protected String orderByColumn;
|
||||
|
||||
/**
|
||||
* 排序的方向desc或者asc
|
||||
*/
|
||||
private String isAsc;
|
||||
|
||||
/**
|
||||
* 当前记录起始索引 默认值
|
||||
*/
|
||||
public static final int DEFAULT_PAGE_NUM = 1;
|
||||
|
||||
/**
|
||||
* 每页显示记录数 默认值 默认查全部
|
||||
*/
|
||||
public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;
|
||||
protected String isAsc = "desc";
|
||||
|
||||
/**
|
||||
* 构建分页对象
|
||||
*/
|
||||
public <T> Page<T> build() {
|
||||
public <T> Page<T> toPage() {
|
||||
Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM);
|
||||
Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE);
|
||||
if (pageNum <= 0) {
|
||||
@@ -70,7 +69,7 @@ public class PageQuery implements Serializable {
|
||||
}
|
||||
Page<T> page = new Page<>(pageNum, pageSize);
|
||||
List<OrderItem> orderItems = buildOrderItem();
|
||||
if (CollUtil.isNotEmpty(orderItems)) {
|
||||
if (CollectionUtils.isNotEmpty(orderItems)) {
|
||||
page.addOrder(orderItems);
|
||||
}
|
||||
return page;
|
||||
@@ -127,4 +126,5 @@ public class PageQuery implements Serializable {
|
||||
this.pageNum = pageNum;
|
||||
}
|
||||
|
||||
public abstract LambdaQueryWrapper<T> toQueryWrapper();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.agileboot.common.mybatis.core.page;
|
||||
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分页模型类
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Data
|
||||
public class PageR<T> {
|
||||
private List<T> rows; // 列表数据
|
||||
private Long total; // 总记录数
|
||||
private Long current; // 当前页数
|
||||
private Long size; // 每页记录数
|
||||
private Long pages; // 总页数
|
||||
|
||||
private int code;
|
||||
private String msg;
|
||||
|
||||
public PageR() {
|
||||
this.code = HttpStatus.HTTP_OK;
|
||||
this.msg = "查询成功";
|
||||
this.total = 0L;
|
||||
this.current = 0L;
|
||||
this.size = 0L;
|
||||
this.pages = 0L;
|
||||
}
|
||||
|
||||
public PageR(List<T> list) {
|
||||
this.code = HttpStatus.HTTP_OK;
|
||||
this.rows = list;
|
||||
this.total = (long) list.size();
|
||||
this.msg = "查询成功";
|
||||
}
|
||||
|
||||
public PageR(IPage<T> page) {
|
||||
this.code = HttpStatus.HTTP_OK;
|
||||
this.msg = "查询成功";
|
||||
this.rows = page.getRecords();
|
||||
this.total = page.getTotal();
|
||||
this.current = page.getCurrent();
|
||||
this.size = page.getSize();
|
||||
this.pages = page.getPages();
|
||||
}
|
||||
|
||||
public PageR(IPage<?> page, List<T> list) {
|
||||
this.code = HttpStatus.HTTP_OK;
|
||||
this.msg = "查询成功";
|
||||
if (CollectionUtils.isEmpty(list)) {
|
||||
this.total = 0L;
|
||||
this.current = 0L;
|
||||
this.size = 0L;
|
||||
this.pages = 0L;
|
||||
} else {
|
||||
this.rows = list;
|
||||
this.total = page.getTotal();
|
||||
this.current = page.getCurrent();
|
||||
this.size = page.getSize();
|
||||
this.pages = page.getPages();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
package com.agileboot.common.mybatis.core.page;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 表格分页数据对象
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class TableDataInfo<T> implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 总记录数
|
||||
*/
|
||||
private long total;
|
||||
|
||||
/**
|
||||
* 列表数据
|
||||
*/
|
||||
private List<T> rows;
|
||||
|
||||
/**
|
||||
* 消息状态码
|
||||
*/
|
||||
private int code;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* 分页
|
||||
*
|
||||
* @param list 列表数据
|
||||
* @param total 总记录数
|
||||
*/
|
||||
public TableDataInfo(List<T> list, long total) {
|
||||
this.rows = list;
|
||||
this.total = total;
|
||||
this.code = HttpStatus.HTTP_OK;
|
||||
this.msg = "查询成功";
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据分页对象构建表格分页数据对象
|
||||
*/
|
||||
public static <T> TableDataInfo<T> build(IPage<T> page) {
|
||||
TableDataInfo<T> rspData = new TableDataInfo<>();
|
||||
rspData.setCode(HttpStatus.HTTP_OK);
|
||||
rspData.setMsg("查询成功");
|
||||
rspData.setRows(page.getRecords());
|
||||
rspData.setTotal(page.getTotal());
|
||||
return rspData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据数据列表构建表格分页数据对象
|
||||
*/
|
||||
public static <T> TableDataInfo<T> build(List<T> list) {
|
||||
TableDataInfo<T> rspData = new TableDataInfo<>();
|
||||
rspData.setCode(HttpStatus.HTTP_OK);
|
||||
rspData.setMsg("查询成功");
|
||||
rspData.setRows(list);
|
||||
rspData.setTotal(list.size());
|
||||
return rspData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建表格分页数据对象
|
||||
*/
|
||||
public static <T> TableDataInfo<T> build() {
|
||||
TableDataInfo<T> rspData = new TableDataInfo<>();
|
||||
rspData.setCode(HttpStatus.HTTP_OK);
|
||||
rspData.setMsg("查询成功");
|
||||
return rspData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据原始数据列表和分页参数,构建表格分页数据对象(用于假分页)
|
||||
*
|
||||
* @param list 原始数据列表(全部数据)
|
||||
* @param page 分页参数对象(包含当前页码、每页大小等)
|
||||
* @return 构造好的分页结果 TableDataInfo<T>
|
||||
*/
|
||||
public static <T> TableDataInfo<T> build(List<T> list, IPage<T> page) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return TableDataInfo.build();
|
||||
}
|
||||
List<T> pageList = CollUtil.page((int) page.getCurrent() - 1, (int) page.getSize(), list);
|
||||
return new TableDataInfo<>(pageList, list.size());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.agileboot.common.mybatis.handler;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import com.agileboot.common.core.exception.BizException;
|
||||
import com.agileboot.common.mybatis.core.domain.BaseEntity;
|
||||
import com.agileboot.common.satoken.pojo.LoginUser;
|
||||
import com.agileboot.common.satoken.utils.LoginHelper;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* MP注入处理器
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
public class InjectionMetaObjectHandler implements MetaObjectHandler {
|
||||
|
||||
/**
|
||||
* 如果用户不存在默认注入-1代表无用户
|
||||
*/
|
||||
private static final Long DEFAULT_USER_ID = -1L;
|
||||
|
||||
/**
|
||||
* 插入填充方法,用于在插入数据时自动填充实体对象中的创建时间、更新时间、创建人、更新人等信息
|
||||
*
|
||||
* @param metaObject 元对象,用于获取原始对象并进行填充
|
||||
*/
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
try {
|
||||
if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) {
|
||||
Date current = ObjectUtils.defaultIfNull(baseEntity.getCreateTime(), new Date());
|
||||
baseEntity.setCreateTime(current);
|
||||
// baseEntity.setUpdateTime(current);
|
||||
baseEntity.setDeleted(0);
|
||||
|
||||
// 如果创建人为空,则填充当前登录用户的信息
|
||||
if (ObjectUtil.isNull(baseEntity.getCreateBy())) {
|
||||
LoginUser loginUser = getLoginUser();
|
||||
if (ObjectUtil.isNotNull(loginUser)) {
|
||||
Long userId = loginUser.getUserId();
|
||||
// 填充创建人、更新人和创建部门信息
|
||||
baseEntity.setCreateBy(userId);
|
||||
// baseEntity.setUpdateBy(userId);
|
||||
} else {
|
||||
// 填充创建人、更新人和创建部门信息
|
||||
baseEntity.setCreateBy(DEFAULT_USER_ID);
|
||||
// baseEntity.setUpdateBy(DEFAULT_USER_ID);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Date date = new Date();
|
||||
this.strictInsertFill(metaObject, "createTime", Date.class, date);
|
||||
// this.strictInsertFill(metaObject, "updateTime", Date.class, date);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new BizException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新填充方法,用于在更新数据时自动填充实体对象中的更新时间和更新人信息
|
||||
*
|
||||
* @param metaObject 元对象,用于获取原始对象并进行填充
|
||||
*/
|
||||
@Override
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
try {
|
||||
if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) {
|
||||
// 获取当前时间作为更新时间,无论原始对象中的更新时间是否为空都填充
|
||||
Date current = new Date();
|
||||
baseEntity.setUpdateTime(current);
|
||||
|
||||
// 获取当前登录用户的ID,并填充更新人信息
|
||||
Long userId = LoginHelper.getUserId();
|
||||
if (ObjectUtil.isNotNull(userId)) {
|
||||
baseEntity.setUpdateBy(userId);
|
||||
} else {
|
||||
baseEntity.setUpdateBy(DEFAULT_USER_ID);
|
||||
}
|
||||
} else {
|
||||
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new BizException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户信息
|
||||
*
|
||||
* @return 当前登录用户的信息,如果用户未登录则返回 null
|
||||
*/
|
||||
private LoginUser getLoginUser() {
|
||||
LoginUser loginUser;
|
||||
try {
|
||||
loginUser = LoginHelper.getLoginUser();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
return loginUser;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.agileboot.common.mybatis.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @Author cuiJiaWang
|
||||
* @Create 2025-09-22 16:41
|
||||
*/
|
||||
public interface BaseMapperDelete<T> extends BaseMapper<T> {
|
||||
|
||||
/**
|
||||
* 物理删除
|
||||
*/
|
||||
int deleteAbsoluteById(Serializable id);
|
||||
|
||||
/**
|
||||
* 批量物理删除
|
||||
*/
|
||||
int deleteAbsoluteByIds(@Param(Constants.COLL) Collection<?> idList);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.agileboot.common.mybatis.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
|
||||
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author cuiJiaWang
|
||||
* @Create 2025-09-22 16:46
|
||||
*/
|
||||
public class CustomSqlInjector extends DefaultSqlInjector {
|
||||
@Override
|
||||
public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
|
||||
List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
|
||||
methodList.add(new DeleteAbsoluteById());
|
||||
methodList.add(new DeleteAbsoluteByIds());
|
||||
return methodList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.agileboot.common.mybatis.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.enums.SqlMethod;
|
||||
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.SqlSource;
|
||||
|
||||
/**
|
||||
* @Author cuiJiaWang
|
||||
* @Create 2025-09-22 16:45
|
||||
*/
|
||||
public class DeleteAbsoluteById extends AbstractMethod {
|
||||
|
||||
private static final String method = "deleteAbsoluteById";
|
||||
|
||||
public DeleteAbsoluteById() {
|
||||
super(method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
|
||||
SqlMethod sqlMethod = SqlMethod.DELETE_BY_ID;
|
||||
String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty());
|
||||
SqlSource sqlSource = this.languageDriver.createSqlSource(this.configuration, sql, Object.class);
|
||||
return this.addDeleteMappedStatement(mapperClass, method, sqlSource);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.agileboot.common.mybatis.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.enums.SqlMethod;
|
||||
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
||||
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.SqlSource;
|
||||
|
||||
/**
|
||||
* @Author cuiJiaWang
|
||||
* @Create 2025-09-22 16:49
|
||||
*/
|
||||
public class DeleteAbsoluteByIds extends AbstractMethod {
|
||||
|
||||
private static final String method = "deleteAbsoluteByIds";
|
||||
|
||||
public DeleteAbsoluteByIds() {
|
||||
super(method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
|
||||
SqlMethod sqlMethod = SqlMethod.DELETE_BY_IDS;
|
||||
String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), tableInfo.getKeyColumn(), SqlScriptUtils.convertForeach(SqlScriptUtils.convertChoose("@org.apache.ibatis.type.SimpleTypeRegistry@isSimpleType(item.getClass())", "#{item}", "#{item." + tableInfo.getKeyProperty() + "}"), "coll", (String) null, "item", ","));
|
||||
SqlSource sqlSource = this.languageDriver.createSqlSource(this.configuration, sql, Object.class);
|
||||
return this.addDeleteMappedStatement(mapperClass, method, sqlSource);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
# MyBatisPlus配置
|
||||
# https://baomidou.com/config/
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath*:com/agileboot/**/mapper/xml/*Mapper.xml
|
||||
mapperPackage: com.agileboot.**.mapper*
|
||||
# 启动时是否检查 MyBatis XML 文件的存在,默认不检查
|
||||
checkConfigLocation: false
|
||||
configuration:
|
||||
@@ -16,13 +18,70 @@ mybatis-plus:
|
||||
# 更详细的日志输出 会有性能损耗 org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
# 关闭日志记录 (可单纯使用 p6spy 分析) org.apache.ibatis.logging.nologging.NoLoggingImpl
|
||||
# 默认日志输出 org.apache.ibatis.logging.slf4j.Slf4jImpl
|
||||
logImpl: org.apache.ibatis.logging.nologging.NoLoggingImpl
|
||||
logImpl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
global-config:
|
||||
banner: true # 是否打印 Logo banner
|
||||
dbConfig:
|
||||
idType: ASSIGN_ID # 主键类型: AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID
|
||||
table-underline: true # 默认数据库表下划线命名
|
||||
logic-delete-field: deleted # 全局逻辑删除字段名
|
||||
logicDeleteValue: 1 # 逻辑已删除值(框架表均使用此值 禁止随意修改)
|
||||
logicNotDeleteValue: 0 # 逻辑未删除值
|
||||
insertStrategy: NOT_NULL
|
||||
updateStrategy: NOT_NULL
|
||||
whereStrategy: NOT_NULL
|
||||
|
||||
# 数据源配置
|
||||
spring:
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
druid:
|
||||
webStatFilter:
|
||||
enabled: true
|
||||
statViewServlet:
|
||||
enabled: true
|
||||
# 设置白名单,不填则允许所有访问
|
||||
allow:
|
||||
url-pattern: /druid/*
|
||||
# 控制台管理用户名和密码
|
||||
login-username: agileboot
|
||||
login-password: 123456
|
||||
filter:
|
||||
stat:
|
||||
enabled: true
|
||||
# 慢SQL记录
|
||||
log-slow-sql: true
|
||||
slow-sql-millis: 1000
|
||||
merge-sql: true
|
||||
wall:
|
||||
config:
|
||||
multi-statement-allow: true
|
||||
dynamic:
|
||||
primary: master
|
||||
strict: false
|
||||
druid:
|
||||
# 初始连接数
|
||||
initialSize: 5
|
||||
# 最小连接池数量
|
||||
minIdle: 10
|
||||
# 最大连接池数量
|
||||
maxActive: 20
|
||||
# 配置获取连接等待超时的时间
|
||||
maxWait: 60000
|
||||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
|
||||
timeBetweenEvictionRunsMillis: 60000
|
||||
# 配置一个连接在池中最小生存的时间,单位是毫秒
|
||||
minEvictableIdleTimeMillis: 300000
|
||||
# 配置一个连接在池中最大生存的时间,单位是毫秒
|
||||
maxEvictableIdleTimeMillis: 900000
|
||||
# 配置检测连接是否有效
|
||||
validationQuery: SELECT 1 FROM DUAL
|
||||
testWhileIdle: true
|
||||
testOnBorrow: false
|
||||
testOnReturn: false
|
||||
datasource:
|
||||
master:
|
||||
url: jdbc:mysql://${wol.mysql.maser.url}/agileboot?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
|
||||
username: ${wol.mysql.maser.username}
|
||||
password: ${wol.mysql.maser.password}
|
||||
|
||||
38
agileboot-common/wol-common-nacos/pom.xml
Normal file
38
agileboot-common/wol-common-nacos/pom.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.agileboot</groupId>
|
||||
<artifactId>agileboot-common</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>wol-common-nacos</artifactId>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Nacos 服务注册与发现-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- 配置中心 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot 3 + Spring Cloud 2023 默认不启用 bootstrap -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,22 @@
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: ${wol.nacos.server.addr}
|
||||
username: ${wol.nacos.server.username}
|
||||
password: ${wol.nacos.server.password}
|
||||
group: ${wol.nacos.server.group}
|
||||
namespace: ${wol.nacos.server.namespace}
|
||||
config:
|
||||
server-addr: ${wol.nacos.server.addr}
|
||||
group: ${wol.nacos.server.group}
|
||||
namespace: ${wol.nacos.server.namespace}
|
||||
file-extension: yaml
|
||||
refresh-enabled: true # 是否开启动态刷新
|
||||
name: ${spring.application.name}
|
||||
username: ${wol.nacos.server.username}
|
||||
password: ${wol.nacos.server.password}
|
||||
config:
|
||||
import:
|
||||
- optional:nacos:${spring.application.name}.${spring.cloud.nacos.config.file-extension}
|
||||
|
||||
@@ -27,7 +27,10 @@
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
@@ -37,6 +40,19 @@
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- JetCache -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.alicp.jetcache</groupId>-->
|
||||
<!-- <artifactId>jetcache-starter-redisson</artifactId>-->
|
||||
<!-- <version>2.7.5</version>-->
|
||||
<!-- <exclusions>-->
|
||||
<!-- <exclusion>-->
|
||||
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||
<!-- <artifactId>spring-boot-starter-logging</artifactId>-->
|
||||
<!-- </exclusion>-->
|
||||
<!-- </exclusions>-->
|
||||
<!-- </dependency>-->
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.agileboot.common.redis.config;
|
||||
|
||||
import com.agileboot.common.core.factory.YmlPropertySourceFactory;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
@@ -12,6 +13,7 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||
@@ -19,6 +21,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
@Slf4j
|
||||
@AutoConfiguration
|
||||
@PropertySource(value = "classpath:common-cache.yml", factory = YmlPropertySourceFactory.class)
|
||||
public class RedisConfiguration {
|
||||
|
||||
@Bean
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user