feat: 自定义流水线

This commit is contained in:
2025-04-16 20:55:56 +08:00
parent 93c51f5c49
commit 557d1ab373
33 changed files with 695 additions and 101 deletions
+2 -6
View File
@@ -4,18 +4,14 @@
<parent>
<artifactId>cutejava</artifactId>
<groupId>cn.odboy</groupId>
<version>1.3.0</version>
<version>1.3.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<properties>
<hutool.version>5.8.36</hutool.version>
</properties>
<artifactId>cutejava-common</artifactId>
<name>公共模块</name>
<dependencies>
<!--工具包-->
<!-- 工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>cutejava</artifactId>
<groupId>cn.odboy</groupId>
<version>1.3.0</version>
<version>1.3.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<properties>
@@ -18,7 +18,7 @@
<dependency>
<groupId>cn.odboy</groupId>
<artifactId>cutejava-common</artifactId>
<version>1.3.0</version>
<version>1.3.1</version>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,67 @@
package cn.odboy.model.pipeline.model;
import cn.odboy.base.MyObject;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = false)
public class PipelineNodeVo extends MyObject {
/**
* 业务节点名称
*/
private String name;
/**
* 是否可以点击
*/
private Boolean click = false;
/**
* 功能按钮列表,与click互斥
*/
private List<PipelineNodeOperationButton> buttonList;
/**
* 业务编码(createReleaseBranch、mergeCode)
*/
private String bizCode;
/**
* 业务节点开始时间
*/
private Long startTimeMillis;
/**
* 业务节点耗时
*/
private Long durationMillis;
/**
* 业务节点执行状态(success 成功, error 失败, running 运行中)
*/
private String executeStatus;
/**
* 业务节点结果描述(执行成功)
*/
private String resultDesc;
/**
* 流水线节点功能按钮
*/
@Data
@EqualsAndHashCode(callSuper = false)
public static class PipelineNodeOperationButton extends MyObject {
/**
* 请求方式(get、post)
*/
private String method;
/**
* 请求路径(/api/doCheck?id=1&bizCode=)
*/
private String requestUrl;
/**
* 按钮名称(通过、打回)
*/
private String text;
/**
* 按钮类型(execute 请求url、link 跳转三方)
*/
private String type;
}
}
@@ -9,7 +9,7 @@ import lombok.EqualsAndHashCode;
@Data
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class MenuMetaModel extends MyObject {
public class MenuMetaVo extends MyObject {
@ApiModelProperty(value = "菜单标题")
private String title;
@@ -12,7 +12,7 @@ import org.springframework.security.core.GrantedAuthority;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RoleCodeModel implements GrantedAuthority {
public class RoleCodeVo implements GrantedAuthority {
@ApiModelProperty(value = "角色名")
private String authority;
@@ -12,7 +12,7 @@ import java.util.Set;
@Data
@EqualsAndHashCode(callSuper = false)
public class SimpleUserModel extends MyObject {
public class SimpleUserVo extends MyObject {
@ApiModelProperty(value = "ID", hidden = true)
private Long id;
@ApiModelProperty(value = "用户角色")
@@ -12,7 +12,7 @@ import java.util.stream.Collectors;
@Getter
@AllArgsConstructor
public class UserJwtModel implements UserDetails {
public class UserJwtVo implements UserDetails {
@ApiModelProperty(value = "用户")
private final User user;
@@ -21,10 +21,10 @@ public class UserJwtModel implements UserDetails {
private final List<Long> dataScopes;
@ApiModelProperty(value = "角色")
private final List<RoleCodeModel> authorities;
private final List<RoleCodeVo> authorities;
public Set<String> getRoles() {
return authorities.stream().map(RoleCodeModel::getAuthority).collect(Collectors.toSet());
return authorities.stream().map(RoleCodeVo::getAuthority).collect(Collectors.toSet());
}
@Override
@@ -8,7 +8,7 @@ import java.util.Date;
* 在线用户
*/
@Data
public class UserOnlineModel {
public class UserOnlineVo {
@ApiModelProperty(value = "Token编号")
private String uid;
@@ -1,7 +1,7 @@
package cn.odboy.model.system.response;
import cn.odboy.base.MyObject;
import cn.odboy.model.system.model.MenuMetaModel;
import cn.odboy.model.system.model.MenuMetaVo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -33,7 +33,7 @@ public class MenuResponse extends MyObject {
private Boolean alwaysShow;
@ApiModelProperty(value = "元数据")
private MenuMetaModel meta;
private MenuMetaVo meta;
@ApiModelProperty(value = "子路由")
private List<MenuResponse> children;
@@ -1,8 +1,8 @@
package cn.odboy.model.system.response;
import cn.odboy.base.MyObject;
import cn.odboy.model.system.model.RoleCodeModel;
import cn.odboy.model.system.model.SimpleUserModel;
import cn.odboy.model.system.model.RoleCodeVo;
import cn.odboy.model.system.model.SimpleUserVo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@@ -12,11 +12,11 @@ import java.util.stream.Collectors;
@Data
@EqualsAndHashCode(callSuper = false)
public class UserInfoResponse extends MyObject {
private SimpleUserModel user;
private SimpleUserVo user;
private List<Long> dataScopes;
private List<RoleCodeModel> authorities;
private List<RoleCodeVo> authorities;
public Set<String> getRoles() {
return authorities.stream().map(RoleCodeModel::getAuthority).collect(Collectors.toSet());
return authorities.stream().map(RoleCodeVo::getAuthority).collect(Collectors.toSet());
}
}
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>cutejava</artifactId>
<groupId>cn.odboy</groupId>
<version>1.3.0</version>
<version>1.3.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -15,7 +15,7 @@
<dependency>
<groupId>cn.odboy</groupId>
<artifactId>cutejava-tools</artifactId>
<version>1.3.0</version>
<version>1.3.1</version>
</dependency>
<!-- quartz -->
+28
View File
@@ -0,0 +1,28 @@
<?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>cutejava</artifactId>
<groupId>cn.odboy</groupId>
<version>1.3.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cutejava-pipline</artifactId>
<name>流水线模块</name>
<dependencies>
<!-- 工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.odboy</groupId>
<artifactId>cutejava-domain</artifactId>
<version>1.3.1</version>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,89 @@
package cn.odboy.context;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
import cn.odboy.exception.BadRequestException;
import cn.odboy.handler.SerialPipelineNodeHandler;
import cn.odboy.model.pipeline.model.PipelineNodeVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* 流水线管理工具
*
* @author odboy
*/
@Slf4j
@Component
public class PipelineManage {
private static final PipelineTaskPool pipelineTaskPool = new PipelineTaskPool("DefaultPipelineTaskPool");
public void execute() {
String pipelineId = IdUtil.objectId();
pipelineTaskPool.submitTask(pipelineId, () -> {
List<PipelineNodeVo> pipelineNodeVos = initialize();
for (PipelineNodeVo pipelineNodeVo : pipelineNodeVos) {
if (Thread.currentThread().isInterrupted()) {
log.info("流水线被强行终止...");
break;
}
executePipeline(pipelineId, pipelineNodeVo);
while (!Thread.currentThread().isInterrupted() && "running".equals(pipelineNodeVo.getExecuteStatus())) {
log.info("流水线执行中...");
ThreadUtil.safeSleep(2000);
}
}
});
ThreadUtil.safeSleep(1000000);
}
private void executePipeline(String pipelineId, PipelineNodeVo pipelineNodeVo) {
String pipelineServiceName = String.format("pipeline:%s", pipelineNodeVo.getBizCode());
SerialPipelineNodeHandler<Object, Object> pipelineNodeHandler = SpringBeanHolder.getBean(pipelineServiceName);
try {
Object o = pipelineNodeHandler.doProcess(pipelineId, null);
int i = 1 / 0;
log.info("运行结果, {}", o);
} catch (BadRequestException e) {
log.error("运行流水线异常", e);
if (pipelineTaskPool.isTaskRunning(pipelineId)) {
pipelineTaskPool.stopTaskForcibly(pipelineId);
}
} catch (Exception e) {
log.error("其他异常", e);
if (pipelineTaskPool.isTaskRunning(pipelineId)) {
pipelineTaskPool.stopTaskForcibly(pipelineId);
}
}
}
public List<PipelineNodeVo> initialize() {
List<PipelineNodeVo> pipelineNodeVos = new ArrayList<>();
PipelineNodeVo createReleaseBranchNodeVo = new PipelineNodeVo();
createReleaseBranchNodeVo.setName("初始化");
createReleaseBranchNodeVo.setClick(true);
createReleaseBranchNodeVo.setButtonList(null);
createReleaseBranchNodeVo.setBizCode("create_release_branch");
createReleaseBranchNodeVo.setStartTimeMillis(DateTime.now().getTime());
createReleaseBranchNodeVo.setDurationMillis(0L);
createReleaseBranchNodeVo.setExecuteStatus("running");
createReleaseBranchNodeVo.setResultDesc(null);
pipelineNodeVos.add(createReleaseBranchNodeVo);
PipelineNodeVo createReleaseBranchNodeVo1 = new PipelineNodeVo();
createReleaseBranchNodeVo1.setName("初始化1");
createReleaseBranchNodeVo1.setClick(true);
createReleaseBranchNodeVo1.setButtonList(null);
createReleaseBranchNodeVo1.setBizCode("create_release_branch");
createReleaseBranchNodeVo1.setStartTimeMillis(DateTime.now().getTime());
createReleaseBranchNodeVo1.setDurationMillis(0L);
createReleaseBranchNodeVo1.setExecuteStatus("running");
createReleaseBranchNodeVo1.setResultDesc(null);
pipelineNodeVos.add(createReleaseBranchNodeVo1);
return pipelineNodeVos;
}
}
@@ -0,0 +1,235 @@
package cn.odboy.context;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 流水线任务池
*
* @author odboy
*/
public class PipelineTaskPool {
// 默认线程池参数
private static final int DEFAULT_CORE_POOL_SIZE = 5;
private static final int DEFAULT_MAX_POOL_SIZE = 10;
private static final long DEFAULT_KEEP_ALIVE_TIME = 60L;
private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS;
private static final int DEFAULT_QUEUE_CAPACITY = 100;
// 线程池实例
private final ThreadPoolExecutor executor;
// 存储正在运行的任务
private final Map<String, ManagedTask> runningTasks = new ConcurrentHashMap<>();
// 线程池管理器名称
private final String name;
/**
* 使用默认参数创建线程池管理器
*/
public PipelineTaskPool(String name) {
this(name, DEFAULT_CORE_POOL_SIZE, DEFAULT_MAX_POOL_SIZE,
DEFAULT_KEEP_ALIVE_TIME, DEFAULT_TIME_UNIT,
new LinkedBlockingQueue<>(DEFAULT_QUEUE_CAPACITY));
}
/**
* 自定义参数创建线程池管理器
*/
public PipelineTaskPool(String name, int corePoolSize, int maxPoolSize,
long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this.name = name;
this.executor = new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
unit,
workQueue,
new ThreadFactory() {
private final AtomicInteger threadNumber = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, name + "-thread-" + threadNumber.getAndIncrement());
}
},
new ThreadPoolExecutor.AbortPolicy()
);
}
/**
* 提交任务并管理
*
* @param taskId 任务ID
* @param task 任务逻辑
* @return true表示提交成功,false表示任务已存在
*/
public boolean submitTask(String taskId, Runnable task) {
if (runningTasks.containsKey(taskId)) {
return false;
}
ManagedTask managedTask = new ManagedTask(taskId, task);
runningTasks.put(taskId, managedTask);
executor.execute(managedTask);
return true;
}
/**
* 正常停止任务
*
* @param taskId 任务ID
* @return true表示停止成功,false表示任务不存在或已停止
*/
public boolean stopTaskGracefully(String taskId) {
ManagedTask task = runningTasks.get(taskId);
if (task != null) {
return task.stopGracefully();
}
return false;
}
/**
* 强制停止任务
*
* @param taskId 任务ID
* @return true表示停止成功,false表示任务不存在
*/
public boolean stopTaskForcibly(String taskId) {
ManagedTask task = runningTasks.remove(taskId);
if (task != null) {
return task.stopForcibly();
}
return false;
}
/**
* 检查任务是否正在运行
*
* @param taskId 任务ID
* @return true表示正在运行
*/
public boolean isTaskRunning(String taskId) {
ManagedTask task = runningTasks.get(taskId);
return task != null && !task.isStopped();
}
/**
* 获取正在运行的任务数量
*
* @return 运行中任务数
*/
public int getRunningTaskCount() {
return runningTasks.size();
}
/**
* 关闭线程池(等待所有任务完成)
*/
public void shutdown() {
executor.shutdown();
}
/**
* 立即关闭线程池(尝试停止所有任务)
*/
public void shutdownNow() {
executor.shutdownNow();
runningTasks.clear();
}
/**
* 获取线程池状态信息
*
* @return 状态信息字符串
*/
public String getPoolStatus() {
return String.format(
"[%s] PoolStatus: Active=%d, Completed=%d, Task=%d, Queue=%d/%d",
name,
executor.getActiveCount(),
executor.getCompletedTaskCount(),
executor.getTaskCount(),
executor.getQueue().size(),
executor.getQueue().remainingCapacity()
);
}
/**
* 被管理的任务包装类
*/
private class ManagedTask implements Runnable {
private final String taskId;
private final Runnable task;
private final AtomicBoolean running = new AtomicBoolean(true);
private final AtomicBoolean stopped = new AtomicBoolean(false);
private Thread currentThread;
public ManagedTask(String taskId, Runnable task) {
this.taskId = taskId;
this.task = task;
}
@Override
public void run() {
currentThread = Thread.currentThread();
try {
// 执行前检查是否已被停止
if (running.get()) {
task.run();
}
} finally {
// 任务完成后从运行列表中移除
runningTasks.remove(taskId);
stopped.set(true);
}
}
/**
* 正常停止任务
*
* @return 是否成功停止
*/
public boolean stopGracefully() {
if (running.compareAndSet(true, false)) {
return true;
}
return false;
}
/**
* 强制停止任务
*
* @return 是否成功停止
*/
public boolean stopForcibly() {
if (stopped.get()) {
return false;
}
if (running.compareAndSet(true, false)) {
if (currentThread != null) {
currentThread.interrupt();
}
return true;
}
return false;
}
/**
* 检查任务是否已停止
*
* @return true表示已停止
*/
public boolean isStopped() {
return stopped.get();
}
}
}
@@ -0,0 +1,111 @@
package cn.odboy.handler;
import cn.hutool.core.thread.ThreadUtil;
import com.alibaba.fastjson2.JSON;
import io.undertow.util.BadRequestException;
import lombok.extern.slf4j.Slf4j;
/**
* 串行流水线节点处理器
*
* @author odboy
*/
@Slf4j
public abstract class SerialPipelineNodeHandler<I, T> {
/**
* 预处理
*
* @param pipelineId 流水线id
* @param inputModel 输入参数
*/
protected abstract void preProcess(String pipelineId, I inputModel);
/**
* 预处理异常
*
* @param pipelineId 流水线id
* @param inputModel 输入参数
* @param e 异常信息
*/
protected abstract void preProcessException(String pipelineId, I inputModel, Exception e);
/**
* 处理业务
*
* @param pipelineId 流水线id
* @param inputModel 输入参数
*/
protected abstract T process(String pipelineId, I inputModel);
/**
* 处理业务异常
*
* @param pipelineId 流水线id
* @param inputModel 输入参数
* @param e 异常信息
*/
protected abstract void processException(String pipelineId, I inputModel, Exception e);
/**
* 业务后置处理
*
* @param pipelineId 流水线id
* @param inputModel 输入参数
*/
protected abstract void postProcess(String pipelineId, I inputModel, T processResult);
/**
* 业务后置处理异常
*
* @param pipelineId 流水线id
* @param inputModel 输入参数
*/
protected abstract void postProcessException(String pipelineId, I inputModel, T processResult, Exception e);
/**
* 当前节点是否被锁住(卡点功能),返回false解锁
*
* @param pipelineId 流水线id
* @param inputModel 输入参数
*/
protected abstract boolean isLocked(String pipelineId, I inputModel);
/**
* 业务处理
*
* @param pipelineId 流水线id
* @param inputModel 输入参数
* @return T
*/
public T doProcess(String pipelineId, I inputModel) throws BadRequestException {
try {
preProcess(pipelineId, inputModel);
log.info("preProcess,pipelineId={},inputModel={}", pipelineId, JSON.toJSONString(inputModel));
} catch (Exception e) {
preProcessException(pipelineId, inputModel, e);
log.error("preProcessException,pipelineId={},inputModel={}", pipelineId, JSON.toJSONString(inputModel), e);
throw new BadRequestException(e);
}
while (isLocked(pipelineId, inputModel)) {
ThreadUtil.safeSleep(2000);
}
try {
T processResult = process(pipelineId, inputModel);
log.info("process,pipelineId={},inputModel={}", pipelineId, JSON.toJSONString(inputModel));
try {
postProcess(pipelineId, inputModel, processResult);
log.info("postProcess,pipelineId={},inputModel={},processResult={}", pipelineId, JSON.toJSONString(inputModel), JSON.toJSONString(processResult));
return processResult;
} catch (Exception e) {
postProcessException(pipelineId, inputModel, processResult, e);
log.error("postProcessException,pipelineId={},inputModel={},processResult={}", pipelineId, JSON.toJSONString(inputModel), JSON.toJSONString(processResult));
throw new BadRequestException(e);
}
} catch (Exception e) {
processException(pipelineId, inputModel, e);
log.error("processException,pipelineId={},inputModel={}", pipelineId, JSON.toJSONString(inputModel), e);
throw new BadRequestException(e);
}
}
}
@@ -0,0 +1,42 @@
package cn.odboy.service;
import cn.odboy.handler.SerialPipelineNodeHandler;
import org.springframework.stereotype.Service;
@Service("pipeline:create_release_branch")
public class PipelineAppCreateReleaseBranchBiz extends SerialPipelineNodeHandler<Object, Object> {
@Override
public void preProcess(String pipelineId, Object inputModel) {
}
@Override
public void preProcessException(String pipelineId, Object inputModel, Exception e) {
}
@Override
public Object process(String pipelineId, Object inputModel) {
return "Hello World";
}
@Override
public void processException(String pipelineId, Object inputModel, Exception e) {
}
@Override
public void postProcess(String pipelineId, Object inputModel, Object processResult) {
}
@Override
public void postProcessException(String pipelineId, Object inputModel, Object processResult, Exception e) {
}
@Override
public boolean isLocked(String pipelineId, Object inputModel) {
return false;
}
}
+8 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>cutejava</artifactId>
<groupId>cn.odboy</groupId>
<version>1.3.0</version>
<version>1.3.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -15,7 +15,13 @@
<dependency>
<groupId>cn.odboy</groupId>
<artifactId>cutejava-system</artifactId>
<version>1.3.0</version>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>cn.odboy</groupId>
<artifactId>cutejava-pipline</artifactId>
<version>1.3.1</version>
</dependency>
</dependencies>
@@ -0,0 +1,18 @@
package cn.odboy.util;
import cn.odboy.context.PipelineManage;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PipelineTests {
@Autowired
private PipelineManage pipelineManage;
@Test
public void contextLoads() {
pipelineManage.execute();
}
}
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>cutejava</artifactId>
<groupId>cn.odboy</groupId>
<version>1.3.0</version>
<version>1.3.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -21,7 +21,7 @@
<dependency>
<groupId>cn.odboy</groupId>
<artifactId>cutejava-job</artifactId>
<version>1.3.0</version>
<version>1.3.1</version>
</dependency>
<!-- jwt -->
@@ -3,7 +3,7 @@ package cn.odboy.application.core.context;
import cn.hutool.core.util.StrUtil;
import cn.odboy.application.core.service.impl.UserOnlineServiceImpl;
import cn.odboy.constant.SystemConst;
import cn.odboy.model.system.model.UserOnlineModel;
import cn.odboy.model.system.model.UserOnlineVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
@@ -38,9 +38,9 @@ public class TokenFilter extends GenericFilterBean {
if (StrUtil.isNotBlank(token)) {
// 获取用户Token的Key
String loginKey = tokenProvider.loginKey(token);
UserOnlineModel userOnlineModel = onlineUserService.describeUserOnlineModelByKey(loginKey);
UserOnlineVo userOnlineVo = onlineUserService.describeUserOnlineModelByKey(loginKey);
// 判断用户在线信息是否为空
if (userOnlineModel != null) {
if (userOnlineVo != null) {
// Token 续期判断
tokenProvider.checkRenewal(token);
// 获取认证信息,设置上下文
@@ -6,7 +6,7 @@ import cn.hutool.core.util.IdUtil;
import cn.odboy.config.AppProperties;
import cn.odboy.constant.SystemConst;
import cn.odboy.constant.SystemRedisKey;
import cn.odboy.model.system.model.UserJwtModel;
import cn.odboy.model.system.model.UserJwtVo;
import cn.odboy.redis.RedisHelper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
@@ -61,7 +61,7 @@ public class TokenProvider implements InitializingBean {
* @param user /
* @return /
*/
public String createToken(UserJwtModel user) {
public String createToken(UserJwtVo user) {
// 设置参数
Map<String, Object> claims = new HashMap<>(6);
// 设置用户ID
@@ -12,7 +12,7 @@ import cn.odboy.constant.SystemRedisKey;
import cn.odboy.config.AppProperties;
import cn.odboy.context.SecurityHelper;
import cn.odboy.exception.BadRequestException;
import cn.odboy.model.system.model.UserJwtModel;
import cn.odboy.model.system.model.UserJwtVo;
import cn.odboy.model.system.request.UserLoginRequest;
import cn.odboy.model.system.response.UserInfoResponse;
import cn.odboy.redis.RedisHelper;
@@ -71,7 +71,7 @@ public class AuthController {
throw new BadRequestException("验证码错误");
}
// 获取用户信息
UserJwtModel jwtUser = userDetailsService.loadUserByUsername(loginRequest.getUsername());
UserJwtVo jwtUser = userDetailsService.loadUserByUsername(loginRequest.getUsername());
// 验证用户密码
if (!passwordEncoder.matches(password, jwtUser.getPassword())) {
throw new BadRequestException("登录密码错误");
@@ -98,7 +98,7 @@ public class AuthController {
@ApiOperation("获取用户信息")
@PostMapping(value = "/info")
public ResponseEntity<UserInfoResponse> getUserInfo() {
UserJwtModel jwtUser = (UserJwtModel) SecurityHelper.getCurrentUser();
UserJwtVo jwtUser = (UserJwtVo) SecurityHelper.getCurrentUser();
UserInfoResponse userInfoResponse = BeanUtil.copyProperties(jwtUser, UserInfoResponse.class);
return ResponseEntity.ok(userInfoResponse);
}
@@ -2,7 +2,7 @@ package cn.odboy.application.core.rest;
import cn.odboy.application.core.service.UserOnlineService;
import cn.odboy.base.PageResult;
import cn.odboy.model.system.model.UserOnlineModel;
import cn.odboy.model.system.model.UserOnlineVo;
import cn.odboy.util.DesEncryptUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -31,7 +31,7 @@ public class OnlineController {
@ApiOperation("查询在线用户")
@GetMapping
@PreAuthorize("@el.check()")
public ResponseEntity<PageResult<UserOnlineModel>> queryOnlineUser(String username, Pageable pageable) {
public ResponseEntity<PageResult<UserOnlineVo>> queryOnlineUser(String username, Pageable pageable) {
return new ResponseEntity<>(userOnlineService.describeUserOnlineModelPage(username, pageable), HttpStatus.OK);
}
@@ -1,6 +1,6 @@
package cn.odboy.application.core.service;
import cn.odboy.model.system.model.UserJwtModel;
import cn.odboy.model.system.model.UserJwtVo;
import org.springframework.scheduling.annotation.Async;
public interface UserCacheService {
@@ -8,9 +8,9 @@ public interface UserCacheService {
* 返回用户缓存
*
* @param username 用户名
* @return UserJwtModel
* @return UserJwtVo
*/
UserJwtModel describeUserJwtModelByUsername(String username);
UserJwtVo describeUserJwtModelByUsername(String username);
/**
* 添加缓存到Redis
@@ -18,7 +18,7 @@ public interface UserCacheService {
* @param userName 用户名
*/
@Async
void saveUserJwtModelByUserName(String userName, UserJwtModel user);
void saveUserJwtModelByUserName(String userName, UserJwtVo user);
/**
* 清理用户缓存信息
@@ -1,8 +1,8 @@
package cn.odboy.application.core.service;
import cn.odboy.base.PageResult;
import cn.odboy.model.system.model.UserJwtModel;
import cn.odboy.model.system.model.UserOnlineModel;
import cn.odboy.model.system.model.UserJwtVo;
import cn.odboy.model.system.model.UserOnlineVo;
import org.springframework.data.domain.Pageable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -13,11 +13,11 @@ public interface UserOnlineService {
/**
* 保存在线用户信息
*
* @param userJwtModel /
* @param userJwtVo /
* @param token /
* @param request /
*/
void saveUserJwtModelByToken(UserJwtModel userJwtModel, String token, HttpServletRequest request);
void saveUserJwtModelByToken(UserJwtVo userJwtVo, String token, HttpServletRequest request);
/**
* 查询全部数据
@@ -26,7 +26,7 @@ public interface UserOnlineService {
* @param pageable /
* @return /
*/
PageResult<UserOnlineModel> describeUserOnlineModelPage(String username, Pageable pageable);
PageResult<UserOnlineVo> describeUserOnlineModelPage(String username, Pageable pageable);
/**
* 查询全部数据,不分页
@@ -34,7 +34,7 @@ public interface UserOnlineService {
* @param username /
* @return /
*/
List<UserOnlineModel> describeUserOnlineModelListByUsername(String username);
List<UserOnlineVo> describeUserOnlineModelListByUsername(String username);
/**
* 退出登录
@@ -50,7 +50,7 @@ public interface UserOnlineService {
* @param response /
* @throws IOException /
*/
void downloadUserOnlineModelExcel(List<UserOnlineModel> all, HttpServletResponse response) throws IOException;
void downloadUserOnlineModelExcel(List<UserOnlineVo> all, HttpServletResponse response) throws IOException;
/**
* 查询用户
@@ -58,7 +58,7 @@ public interface UserOnlineService {
* @param key /
* @return /
*/
UserOnlineModel describeUserOnlineModelByKey(String key);
UserOnlineVo describeUserOnlineModelByKey(String key);
/**
* 根据用户名强退用户
@@ -3,7 +3,7 @@ package cn.odboy.application.core.service.impl;
import cn.hutool.core.util.RandomUtil;
import cn.odboy.application.core.service.UserCacheService;
import cn.odboy.constant.SystemRedisKey;
import cn.odboy.model.system.model.UserJwtModel;
import cn.odboy.model.system.model.UserJwtVo;
import cn.odboy.redis.RedisHelper;
import cn.odboy.util.StringUtil;
import org.springframework.stereotype.Service;
@@ -18,19 +18,19 @@ public class UserCacheServiceImpl implements UserCacheService {
private RedisHelper redisHelper;
@Override
public UserJwtModel describeUserJwtModelByUsername(String username) {
public UserJwtVo describeUserJwtModelByUsername(String username) {
// 转小写
username = StringUtil.lowerCase(username);
if (StringUtil.isNotEmpty(username)) {
// 获取数据
return redisHelper.get(SystemRedisKey.USER_INFO + username, UserJwtModel.class);
return redisHelper.get(SystemRedisKey.USER_INFO + username, UserJwtVo.class);
}
return null;
}
@Override
public void saveUserJwtModelByUserName(String userName, UserJwtModel user) {
public void saveUserJwtModelByUserName(String userName, UserJwtVo user) {
// 转小写
userName = StringUtil.lowerCase(userName);
if (StringUtil.isNotEmpty(userName)) {
@@ -6,8 +6,8 @@ import cn.odboy.application.system.service.RoleService;
import cn.odboy.application.system.service.UserService;
import cn.odboy.exception.BadRequestException;
import cn.odboy.model.system.domain.User;
import cn.odboy.model.system.model.RoleCodeModel;
import cn.odboy.model.system.model.UserJwtModel;
import cn.odboy.model.system.model.RoleCodeVo;
import cn.odboy.model.system.model.UserJwtVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.userdetails.UserDetailsService;
@@ -24,9 +24,9 @@ public class UserDetailsServiceImpl implements UserDetailsService {
private final UserCacheService userCacheService;
@Override
public UserJwtModel loadUserByUsername(String username) {
UserJwtModel userJwtModel = userCacheService.describeUserJwtModelByUsername(username);
if (userJwtModel == null) {
public UserJwtVo loadUserByUsername(String username) {
UserJwtVo userJwtVo = userCacheService.describeUserJwtModelByUsername(username);
if (userJwtVo == null) {
User user = userService.describeUserByUsername(username);
if (user == null) {
throw new BadRequestException("用户不存在");
@@ -35,13 +35,13 @@ public class UserDetailsServiceImpl implements UserDetailsService {
throw new BadRequestException("账号未激活!");
}
// 获取用户的权限
List<RoleCodeModel> authorities = roleService.buildUserRolePermissions(user);
List<RoleCodeVo> authorities = roleService.buildUserRolePermissions(user);
// 初始化JwtUserDto
userJwtModel = new UserJwtModel(user, dataService.describeDeptIdListByUserIdWithDeptId(user), authorities);
userJwtVo = new UserJwtVo(user, dataService.describeDeptIdListByUserIdWithDeptId(user), authorities);
// 添加缓存数据
userCacheService.saveUserJwtModelByUserName(username, userJwtModel);
userCacheService.saveUserJwtModelByUserName(username, userJwtVo);
}
}
return userJwtModel;
return userJwtVo;
}
}
@@ -5,8 +5,8 @@ import cn.odboy.application.core.service.UserOnlineService;
import cn.odboy.base.PageResult;
import cn.odboy.config.AppProperties;
import cn.odboy.constant.SystemRedisKey;
import cn.odboy.model.system.model.UserJwtModel;
import cn.odboy.model.system.model.UserOnlineModel;
import cn.odboy.model.system.model.UserJwtVo;
import cn.odboy.model.system.model.UserOnlineVo;
import cn.odboy.redis.RedisHelper;
import cn.odboy.util.BrowserUtil;
import cn.odboy.util.DesEncryptUtil;
@@ -39,46 +39,46 @@ public class UserOnlineServiceImpl implements UserOnlineService {
private final RedisHelper redisHelper;
@Override
public void saveUserJwtModelByToken(UserJwtModel userJwtModel, String token, HttpServletRequest request) {
String dept = userJwtModel.getUser().getDept().getName();
public void saveUserJwtModelByToken(UserJwtVo userJwtVo, String token, HttpServletRequest request) {
String dept = userJwtVo.getUser().getDept().getName();
String ip = BrowserUtil.getIp(request);
String id = tokenProvider.getId(token);
String version = BrowserUtil.getVersion(request);
String address = IpUtil.getCityInfo(ip);
UserOnlineModel userOnlineModel = null;
UserOnlineVo userOnlineVo = null;
try {
userOnlineModel = new UserOnlineModel();
userOnlineModel.setUid(id);
userOnlineModel.setUserName(userJwtModel.getUsername());
userOnlineModel.setNickName(userJwtModel.getUser().getNickName());
userOnlineModel.setDept(dept);
userOnlineModel.setBrowser(version);
userOnlineModel.setIp(ip);
userOnlineModel.setAddress(address);
userOnlineModel.setKey(DesEncryptUtil.desEncrypt(token));
userOnlineModel.setLoginTime(new Date());
userOnlineVo = new UserOnlineVo();
userOnlineVo.setUid(id);
userOnlineVo.setUserName(userJwtVo.getUsername());
userOnlineVo.setNickName(userJwtVo.getUser().getNickName());
userOnlineVo.setDept(dept);
userOnlineVo.setBrowser(version);
userOnlineVo.setIp(ip);
userOnlineVo.setAddress(address);
userOnlineVo.setKey(DesEncryptUtil.desEncrypt(token));
userOnlineVo.setLoginTime(new Date());
} catch (Exception e) {
log.error(e.getMessage(), e);
}
String loginKey = tokenProvider.loginKey(token);
redisHelper.set(loginKey, userOnlineModel, properties.getJwt().getTokenValidityInSeconds(), TimeUnit.MILLISECONDS);
redisHelper.set(loginKey, userOnlineVo, properties.getJwt().getTokenValidityInSeconds(), TimeUnit.MILLISECONDS);
}
@Override
public PageResult<UserOnlineModel> describeUserOnlineModelPage(String username, Pageable pageable) {
List<UserOnlineModel> onlineUserList = describeUserOnlineModelListByUsername(username);
List<UserOnlineModel> paging = PageUtil.softPaging(pageable.getPageNumber(), pageable.getPageSize(), onlineUserList);
public PageResult<UserOnlineVo> describeUserOnlineModelPage(String username, Pageable pageable) {
List<UserOnlineVo> onlineUserList = describeUserOnlineModelListByUsername(username);
List<UserOnlineVo> paging = PageUtil.softPaging(pageable.getPageNumber(), pageable.getPageSize(), onlineUserList);
return PageUtil.toPage(paging, onlineUserList.size());
}
@Override
public List<UserOnlineModel> describeUserOnlineModelListByUsername(String username) {
public List<UserOnlineVo> describeUserOnlineModelListByUsername(String username) {
String loginKey = SystemRedisKey.ONLINE_USER + (StringUtil.isBlank(username) ? "" : "*" + username);
List<String> keys = redisHelper.scan(loginKey + "*");
Collections.reverse(keys);
List<UserOnlineModel> onlineUserList = new ArrayList<>();
List<UserOnlineVo> onlineUserList = new ArrayList<>();
for (String key : keys) {
onlineUserList.add(redisHelper.get(key, UserOnlineModel.class));
onlineUserList.add(redisHelper.get(key, UserOnlineVo.class));
}
onlineUserList.sort((o1, o2) -> o2.getLoginTime().compareTo(o1.getLoginTime()));
return onlineUserList;
@@ -91,9 +91,9 @@ public class UserOnlineServiceImpl implements UserOnlineService {
}
@Override
public void downloadUserOnlineModelExcel(List<UserOnlineModel> all, HttpServletResponse response) throws IOException {
public void downloadUserOnlineModelExcel(List<UserOnlineVo> all, HttpServletResponse response) throws IOException {
List<Map<String, Object>> list = new ArrayList<>();
for (UserOnlineModel user : all) {
for (UserOnlineVo user : all) {
Map<String, Object> map = new LinkedHashMap<>();
map.put("用户名", user.getUserName());
map.put("部门", user.getDept());
@@ -107,8 +107,8 @@ public class UserOnlineServiceImpl implements UserOnlineService {
}
@Override
public UserOnlineModel describeUserOnlineModelByKey(String key) {
return redisHelper.get(key, UserOnlineModel.class);
public UserOnlineVo describeUserOnlineModelByKey(String key) {
return redisHelper.get(key, UserOnlineVo.class);
}
@Override
@@ -3,7 +3,7 @@ package cn.odboy.application.system.service;
import cn.odboy.base.PageResult;
import cn.odboy.model.system.domain.Role;
import cn.odboy.model.system.domain.User;
import cn.odboy.model.system.model.RoleCodeModel;
import cn.odboy.model.system.model.RoleCodeVo;
import cn.odboy.model.system.request.CreateRoleRequest;
import cn.odboy.model.system.request.QueryRoleRequest;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -98,7 +98,7 @@ public interface RoleService extends IService<Role> {
* @param user 用户信息
* @return 权限信息
*/
List<RoleCodeModel> buildUserRolePermissions(User user);
List<RoleCodeVo> buildUserRolePermissions(User user);
/**
* 导出数据
@@ -15,7 +15,7 @@ import cn.odboy.exception.EntityExistException;
import cn.odboy.model.system.domain.Menu;
import cn.odboy.model.system.domain.Role;
import cn.odboy.model.system.domain.User;
import cn.odboy.model.system.model.MenuMetaModel;
import cn.odboy.model.system.model.MenuMetaVo;
import cn.odboy.model.system.request.QueryMenuRequest;
import cn.odboy.model.system.response.MenuResponse;
import cn.odboy.redis.RedisHelper;
@@ -273,7 +273,7 @@ public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements Me
menuResponse.setComponent(menu.getComponent());
}
}
menuResponse.setMeta(new MenuMetaModel(menu.getTitle(), menu.getIcon(), !menu.getCache()));
menuResponse.setMeta(new MenuMetaVo(menu.getTitle(), menu.getIcon(), !menu.getCache()));
if (CollectionUtil.isNotEmpty(menuList)) {
menuResponse.setAlwaysShow(true);
menuResponse.setRedirect("noredirect");
@@ -16,7 +16,7 @@ import cn.odboy.exception.EntityExistException;
import cn.odboy.model.system.domain.Menu;
import cn.odboy.model.system.domain.Role;
import cn.odboy.model.system.domain.User;
import cn.odboy.model.system.model.RoleCodeModel;
import cn.odboy.model.system.model.RoleCodeVo;
import cn.odboy.model.system.request.CreateRoleRequest;
import cn.odboy.model.system.request.QueryRoleRequest;
import cn.odboy.redis.RedisHelper;
@@ -167,22 +167,22 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements Ro
}
@Override
public List<RoleCodeModel> buildUserRolePermissions(User user) {
public List<RoleCodeVo> buildUserRolePermissions(User user) {
String key = SystemRedisKey.ROLE_AUTH + user.getId();
List<RoleCodeModel> authorityList = redisHelper.getList(key, RoleCodeModel.class);
List<RoleCodeVo> authorityList = redisHelper.getList(key, RoleCodeVo.class);
if (CollUtil.isEmpty(authorityList)) {
Set<String> permissions = new HashSet<>();
// 如果是管理员直接返回
if (user.getIsAdmin()) {
permissions.add("admin");
return permissions.stream().map(RoleCodeModel::new)
return permissions.stream().map(RoleCodeVo::new)
.collect(Collectors.toList());
}
List<Role> roles = roleMapper.queryRoleListByUserId(user.getId());
permissions = roles.stream().flatMap(role -> role.getMenus().stream())
.map(Menu::getPermission)
.filter(StringUtil::isNotBlank).collect(Collectors.toSet());
authorityList = permissions.stream().map(RoleCodeModel::new)
authorityList = permissions.stream().map(RoleCodeVo::new)
.collect(Collectors.toList());
redisHelper.set(key, authorityList, 1, TimeUnit.HOURS);
}
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<artifactId>cutejava</artifactId>
<groupId>cn.odboy</groupId>
<version>1.3.0</version>
<version>1.3.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -20,7 +20,7 @@
<dependency>
<groupId>cn.odboy</groupId>
<artifactId>cutejava-domain</artifactId>
<version>1.3.0</version>
<version>1.3.1</version>
</dependency>
<!--邮件依赖-->
+3 -1
View File
@@ -5,13 +5,14 @@
<groupId>cn.odboy</groupId>
<artifactId>cutejava</artifactId>
<packaging>pom</packaging>
<version>1.3.0</version>
<version>1.3.1</version>
<modules>
<module>cutejava-common</module>
<module>cutejava-domain</module>
<module>cutejava-tools</module>
<module>cutejava-job</module>
<module>cutejava-pipline</module>
<module>cutejava-system</module>
<module>cutejava-starter</module>
</modules>
@@ -34,6 +35,7 @@
<druid.version>1.2.19</druid.version>
<commons-pool2.version>2.11.1</commons-pool2.version>
<mybatis-plus.version>3.5.10.1</mybatis-plus.version>
<hutool.version>5.8.37</hutool.version>
</properties>
<dependencies>