diff --git a/powerjob-common/src/main/java/tech/powerjob/common/exception/PowerJobException.java b/powerjob-common/src/main/java/tech/powerjob/common/exception/PowerJobException.java index d9ecda6d..3ce68960 100644 --- a/powerjob-common/src/main/java/tech/powerjob/common/exception/PowerJobException.java +++ b/powerjob-common/src/main/java/tech/powerjob/common/exception/PowerJobException.java @@ -1,13 +1,20 @@ package tech.powerjob.common.exception; +import lombok.Getter; +import lombok.Setter; + /** * PowerJob 运行时异常 * * @author tjq * @since 2020/5/26 */ +@Setter +@Getter public class PowerJobException extends RuntimeException { + protected String code; + public PowerJobException() { } diff --git a/powerjob-common/src/main/java/tech/powerjob/common/response/ResultDTO.java b/powerjob-common/src/main/java/tech/powerjob/common/response/ResultDTO.java index 03e1dc6b..677f411d 100644 --- a/powerjob-common/src/main/java/tech/powerjob/common/response/ResultDTO.java +++ b/powerjob-common/src/main/java/tech/powerjob/common/response/ResultDTO.java @@ -18,6 +18,7 @@ import org.apache.commons.lang3.exception.ExceptionUtils; public class ResultDTO implements PowerSerializable { private boolean success; + private String code; private T data; private String message; diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/LoginUserHolder.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/LoginUserHolder.java index 1dd693c5..2ade7416 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/LoginUserHolder.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/LoginUserHolder.java @@ -21,4 +21,16 @@ public class LoginUserHolder { public static void clean() { TL.remove(); } + + /** + * 获取用户名 + * @return 存在则返回常规用户名,否则返回 unknown + */ + public static String getUserName() { + PowerJobUser powerJobUser = get(); + if (powerJobUser != null) { + return powerJobUser.getUsername(); + } + return "UNKNOWN"; + } } diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/common/PowerJobAuthException.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/common/PowerJobAuthException.java index 5a8fbb9f..024dd15f 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/common/PowerJobAuthException.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/common/PowerJobAuthException.java @@ -12,17 +12,12 @@ import tech.powerjob.common.exception.PowerJobException; @Getter public class PowerJobAuthException extends PowerJobException { - private final String code; - - private final String msg; - public PowerJobAuthException(AuthErrorCode errorCode) { - this.code = errorCode.getCode(); - this.msg = errorCode.getMsg(); + this(errorCode, null); } public PowerJobAuthException(AuthErrorCode errorCode, String extraMsg) { + super(extraMsg == null ? errorCode.getMsg() : errorCode.getMsg().concat(":").concat(extraMsg)); this.code = errorCode.getCode(); - this.msg = errorCode.getMsg().concat(":").concat(extraMsg); } } diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/ApiPermission.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/ApiPermission.java index c4f77d83..98bfce2e 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/ApiPermission.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/ApiPermission.java @@ -2,10 +2,6 @@ package tech.powerjob.server.auth.interceptor; import tech.powerjob.server.auth.Permission; import tech.powerjob.server.auth.RoleScope; -import tech.powerjob.server.auth.interceptor.dp.DynamicPermission; -import tech.powerjob.server.auth.interceptor.dp.EmptyDynamicPermission; -import tech.powerjob.server.auth.interceptor.gp.EmptyGrantPermissionPlugin; -import tech.powerjob.server.auth.interceptor.gp.GrantPermissionPlugin; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -39,11 +35,11 @@ public @interface ApiPermission { * 固定权限不支持的场景,需要使用动态权限 * @return 动态权限 */ - Class dynamicPermissionPlugin() default EmptyDynamicPermission.class; + Class dynamicPermissionPlugin() default EmptyPlugin.class; /** * 新增场景,需要授权插件执行授权 * @return 授权插件 */ - Class grandPermissionPlugin() default EmptyGrantPermissionPlugin.class; + Class grandPermissionPlugin() default EmptyPlugin.class; } diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/ApiPermissionAspect.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/ApiPermissionAspect.java index 4b57e2a2..e0179fe2 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/ApiPermissionAspect.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/ApiPermissionAspect.java @@ -9,7 +9,6 @@ import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.stereotype.Component; -import tech.powerjob.server.auth.interceptor.gp.GrantPermissionPlugin; import java.lang.reflect.Method; diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/dp/DynamicPermission.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/DynamicPermissionPlugin.java similarity index 71% rename from powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/dp/DynamicPermission.java rename to powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/DynamicPermissionPlugin.java index baa803d7..504deb6e 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/dp/DynamicPermission.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/DynamicPermissionPlugin.java @@ -1,4 +1,4 @@ -package tech.powerjob.server.auth.interceptor.dp; +package tech.powerjob.server.auth.interceptor; import tech.powerjob.server.auth.Permission; @@ -10,6 +10,6 @@ import javax.servlet.http.HttpServletRequest; * @author tjq * @since 2023/9/3 */ -public interface DynamicPermission { +public interface DynamicPermissionPlugin { Permission calculate(HttpServletRequest request, Object handler); } diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/EmptyPlugin.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/EmptyPlugin.java new file mode 100644 index 00000000..95d00b8f --- /dev/null +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/EmptyPlugin.java @@ -0,0 +1,24 @@ +package tech.powerjob.server.auth.interceptor; + +import tech.powerjob.server.auth.Permission; + +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; + +/** + * 空 + * + * @author tjq + * @since 2024/2/12 + */ +public class EmptyPlugin implements DynamicPermissionPlugin, GrantPermissionPlugin { + @Override + public Permission calculate(HttpServletRequest request, Object handler) { + return null; + } + + @Override + public void grant(Object[] args, Object result, Method method, Object originBean) { + + } +} diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/gp/GrantPermissionPlugin.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/GrantPermissionPlugin.java similarity index 88% rename from powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/gp/GrantPermissionPlugin.java rename to powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/GrantPermissionPlugin.java index 746cd000..34d085ad 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/gp/GrantPermissionPlugin.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/GrantPermissionPlugin.java @@ -1,4 +1,4 @@ -package tech.powerjob.server.auth.interceptor.gp; +package tech.powerjob.server.auth.interceptor; import java.lang.reflect.Method; diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/PowerJobAuthInterceptor.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/PowerJobAuthInterceptor.java index d3c1f54c..b5abdb42 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/PowerJobAuthInterceptor.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/PowerJobAuthInterceptor.java @@ -48,7 +48,7 @@ public class PowerJobAuthInterceptor implements HandlerInterceptor { // 尝试直接解析登陆 final Optional loginUserOpt = powerJobLoginService.ifLogin(request); - // 未登录前先使用302重定向到登录页面 + // 未登录前先使用302重定向到登录页面 TODO: 前端登录还是服务端直接跳转有待考虑 if (!loginUserOpt.isPresent()) { response.setStatus(302); response.setHeader("location", request.getContextPath() + "/login"); diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/dp/EmptyDynamicPermission.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/dp/EmptyDynamicPermission.java deleted file mode 100644 index ccc3f2fd..00000000 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/dp/EmptyDynamicPermission.java +++ /dev/null @@ -1,18 +0,0 @@ -package tech.powerjob.server.auth.interceptor.dp; - -import tech.powerjob.server.auth.Permission; - -import javax.servlet.http.HttpServletRequest; - -/** - * NotUseDynamicPermission - * - * @author tjq - * @since 2023/9/3 - */ -public class EmptyDynamicPermission implements DynamicPermission { - @Override - public Permission calculate(HttpServletRequest request, Object handler) { - return null; - } -} diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/gp/EmptyGrantPermissionPlugin.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/gp/EmptyGrantPermissionPlugin.java deleted file mode 100644 index b50b4599..00000000 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/gp/EmptyGrantPermissionPlugin.java +++ /dev/null @@ -1,16 +0,0 @@ -package tech.powerjob.server.auth.interceptor.gp; - -import java.lang.reflect.Method; - -/** - * do nothing - * - * @author tjq - * @since 2024/2/11 - */ -public class EmptyGrantPermissionPlugin implements GrantPermissionPlugin { - @Override - public void grant(Object[] args, Object result, Method method, Object originBean) { - - } -} diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/permission/PowerJobPermissionService.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/permission/PowerJobPermissionService.java index 3cbeb772..44cb149d 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/permission/PowerJobPermissionService.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/permission/PowerJobPermissionService.java @@ -1,6 +1,8 @@ package tech.powerjob.server.auth.service.permission; import tech.powerjob.server.auth.PowerJobUser; +import tech.powerjob.server.auth.Role; +import tech.powerjob.server.auth.RoleScope; import tech.powerjob.server.auth.interceptor.ApiPermission; import javax.servlet.http.HttpServletRequest; @@ -22,4 +24,14 @@ public interface PowerJobPermissionService { * @return true or false */ boolean hasPermission(HttpServletRequest request, Object handler, PowerJobUser user, ApiPermission apiPermission); + + /** + * 授予用户权限 + * @param roleScope 权限范围 + * @param target 权限目标 + * @param userId 用户ID + * @param role 角色 + * @param extra 其他 + */ + void grantPermission(RoleScope roleScope, Long target, Long userId, Role role, String extra); } diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/permission/PowerJobPermissionServiceImpl.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/permission/PowerJobPermissionServiceImpl.java index 3b0ea263..92a351ff 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/permission/PowerJobPermissionServiceImpl.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/permission/PowerJobPermissionServiceImpl.java @@ -13,8 +13,8 @@ import tech.powerjob.server.auth.PowerJobUser; import tech.powerjob.server.auth.Role; import tech.powerjob.server.auth.RoleScope; import tech.powerjob.server.auth.interceptor.ApiPermission; -import tech.powerjob.server.auth.interceptor.dp.DynamicPermission; -import tech.powerjob.server.auth.interceptor.dp.EmptyDynamicPermission; +import tech.powerjob.server.auth.interceptor.DynamicPermissionPlugin; +import tech.powerjob.server.auth.interceptor.EmptyPlugin; import tech.powerjob.server.persistence.remote.model.AppInfoDO; import tech.powerjob.server.persistence.remote.model.UserRoleDO; import tech.powerjob.server.persistence.remote.repository.AppInfoRepository; @@ -93,6 +93,23 @@ public class PowerJobPermissionServiceImpl implements PowerJobPermissionService return false; } + @Override + public void grantPermission(RoleScope roleScope, Long target, Long userId, Role role, String extra) { + + UserRoleDO userRoleDO = new UserRoleDO(); + userRoleDO.setGmtCreate(new Date()); + userRoleDO.setGmtModified(new Date()); + userRoleDO.setExtra(extra); + + userRoleDO.setScope(roleScope.getV()); + userRoleDO.setTarget(target); + userRoleDO.setUserId(userId); + userRoleDO.setRole(role.getV()); + + userRoleRepository.saveAndFlush(userRoleDO); + log.info("[PowerJobPermissionService] saveAndFlush userRole successfully: {}", userRoleDO); + } + private boolean checkAppPermission(HttpServletRequest request, Permission requiredPermission, Multimap appId2Role, Multimap namespaceId2Role) { final String appIdStr = request.getHeader("appId"); if (StringUtils.isEmpty(appIdStr)) { @@ -144,12 +161,12 @@ public class PowerJobPermissionServiceImpl implements PowerJobPermissionService private static Permission parsePermission(HttpServletRequest request, Object handler, ApiPermission apiPermission) { - Class dynamicPermissionPlugin = apiPermission.dynamicPermissionPlugin(); - if (EmptyDynamicPermission.class.equals(dynamicPermissionPlugin)) { + Class dynamicPermissionPlugin = apiPermission.dynamicPermissionPlugin(); + if (EmptyPlugin.class.equals(dynamicPermissionPlugin)) { return apiPermission.requiredPermission(); } try { - DynamicPermission dynamicPermission = dynamicPermissionPlugin.getDeclaredConstructor().newInstance(); + DynamicPermissionPlugin dynamicPermission = dynamicPermissionPlugin.getDeclaredConstructor().newInstance(); return dynamicPermission.calculate(request, handler); } catch (Throwable t) { log.error("[PowerJobAuthService] process dynamicPermissionPlugin failed!", t); diff --git a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/model/NamespaceDO.java b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/model/NamespaceDO.java index 7abbc8b3..cd2e1de8 100644 --- a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/model/NamespaceDO.java +++ b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/model/NamespaceDO.java @@ -39,6 +39,12 @@ public class NamespaceDO { private Integer status; + /** + * 部门,组织架构相关属性。 + * 预留数据库字段方便基于组织架构二次开发 + */ + private String dept; + /** * 标签,扩展性之王,多值逗号分割 */ diff --git a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/NamespaceRepository.java b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/NamespaceRepository.java index 845f8fca..db7f8e14 100644 --- a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/NamespaceRepository.java +++ b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/NamespaceRepository.java @@ -1,6 +1,7 @@ package tech.powerjob.server.persistence.remote.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import tech.powerjob.server.persistence.remote.model.NamespaceDO; import java.util.Optional; @@ -11,7 +12,7 @@ import java.util.Optional; * @author tjq * @since 2023/9/3 */ -public interface NamespaceRepository extends JpaRepository { +public interface NamespaceRepository extends JpaRepository, JpaSpecificationExecutor { Optional findByCode(String code); } diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/dp/GrantDynamicPermission.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/dp/GrantDynamicPermission.java new file mode 100644 index 00000000..a26bbdbe --- /dev/null +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/dp/GrantDynamicPermission.java @@ -0,0 +1,43 @@ +package tech.powerjob.server.auth.dp; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.StreamUtils; +import tech.powerjob.common.serialize.JsonUtils; +import tech.powerjob.server.auth.Permission; +import tech.powerjob.server.auth.Role; +import tech.powerjob.server.auth.interceptor.DynamicPermissionPlugin; +import tech.powerjob.server.web.request.GrantPermissionRequest; + +import javax.servlet.http.HttpServletRequest; + +/** + * 授权动态权限计算 + * 授予权限要低于或等于授权人自身的权限 + * + * @author tjq + * @since 2024/2/12 + */ +@Slf4j +public class GrantDynamicPermission implements DynamicPermissionPlugin { + @Override + public Permission calculate(HttpServletRequest request, Object handler) { + try { + //获取请求body + byte[] bodyBytes = StreamUtils.copyToByteArray(request.getInputStream()); + String body = new String(bodyBytes, request.getCharacterEncoding()); + GrantPermissionRequest grantPermissionRequest = JsonUtils.parseObject(body, GrantPermissionRequest.class); + Role role = Role.of(grantPermissionRequest.getRole()); + + switch (role) { + case OBSERVER: return Permission.READ; + case QA: return Permission.OPS; + case DEVELOPER: return Permission.WRITE; + } + + } catch (Exception e) { + log.error("[GrantDynamicPermission] check permission failed, please fix the bug!!!", e); + } + + return Permission.SU; + } +} diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/dp/ModifyOrCreateDynamicPermission.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/dp/ModifyOrCreateDynamicPermission.java similarity index 88% rename from powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/dp/ModifyOrCreateDynamicPermission.java rename to powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/dp/ModifyOrCreateDynamicPermission.java index 99afd84d..5d655540 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/dp/ModifyOrCreateDynamicPermission.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/dp/ModifyOrCreateDynamicPermission.java @@ -1,9 +1,10 @@ -package tech.powerjob.server.auth.interceptor.dp; +package tech.powerjob.server.auth.dp; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StreamUtils; import tech.powerjob.common.serialize.JsonUtils; import tech.powerjob.server.auth.Permission; +import tech.powerjob.server.auth.interceptor.DynamicPermissionPlugin; import javax.servlet.http.HttpServletRequest; import java.util.Map; @@ -16,7 +17,7 @@ import java.util.Map; * @since 2023/9/3 */ @Slf4j -public class ModifyOrCreateDynamicPermission implements DynamicPermission { +public class ModifyOrCreateDynamicPermission implements DynamicPermissionPlugin { @Override public Permission calculate(HttpServletRequest request, Object handler) { @@ -36,7 +37,7 @@ public class ModifyOrCreateDynamicPermission implements DynamicPermission { return Permission.WRITE; } catch (Exception e) { - log.error("[ModifyOrCreateDynamicPermission] check permission failed, please fix the bug!!!"); + log.error("[ModifyOrCreateDynamicPermission] check permission failed, please fix the bug!!!", e); } // 异常情况先放行,不影响功能使用,后续修复 BUG diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/gp/SaveAppGrantPermissionPlugin.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/gp/SaveAppGrantPermissionPlugin.java similarity index 84% rename from powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/gp/SaveAppGrantPermissionPlugin.java rename to powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/gp/SaveAppGrantPermissionPlugin.java index 2e123582..38c9deeb 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/gp/SaveAppGrantPermissionPlugin.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/gp/SaveAppGrantPermissionPlugin.java @@ -1,4 +1,4 @@ -package tech.powerjob.server.auth.interceptor.gp; +package tech.powerjob.server.auth.gp; import tech.powerjob.server.auth.RoleScope; diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/gp/SaveGrantPermissionPlugin.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/gp/SaveGrantPermissionPlugin.java similarity index 96% rename from powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/gp/SaveGrantPermissionPlugin.java rename to powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/gp/SaveGrantPermissionPlugin.java index 5ac22f86..ca09dd83 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/gp/SaveGrantPermissionPlugin.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/gp/SaveGrantPermissionPlugin.java @@ -1,4 +1,4 @@ -package tech.powerjob.server.auth.interceptor.gp; +package tech.powerjob.server.auth.gp; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.MapUtils; @@ -8,6 +8,7 @@ import tech.powerjob.server.auth.LoginUserHolder; import tech.powerjob.server.auth.PowerJobUser; import tech.powerjob.server.auth.Role; import tech.powerjob.server.auth.RoleScope; +import tech.powerjob.server.auth.interceptor.GrantPermissionPlugin; import tech.powerjob.server.common.utils.SpringUtils; import tech.powerjob.server.persistence.remote.model.UserRoleDO; import tech.powerjob.server.persistence.remote.repository.UserRoleRepository; diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/gp/SaveNamespaceGrantPermissionPlugin.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/gp/SaveNamespaceGrantPermissionPlugin.java similarity index 85% rename from powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/gp/SaveNamespaceGrantPermissionPlugin.java rename to powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/gp/SaveNamespaceGrantPermissionPlugin.java index d9fbe752..7e2b2d7c 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/interceptor/gp/SaveNamespaceGrantPermissionPlugin.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/auth/gp/SaveNamespaceGrantPermissionPlugin.java @@ -1,4 +1,4 @@ -package tech.powerjob.server.auth.interceptor.gp; +package tech.powerjob.server.auth.gp; import tech.powerjob.server.auth.RoleScope; diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/ControllerExceptionHandler.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/ControllerExceptionHandler.java index 0730312f..8bfcd1b9 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/ControllerExceptionHandler.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/ControllerExceptionHandler.java @@ -1,7 +1,5 @@ package tech.powerjob.server.web; -import tech.powerjob.common.exception.PowerJobException; -import tech.powerjob.common.response.ResultDTO; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.http.converter.HttpMessageNotReadableException; @@ -10,6 +8,8 @@ import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; +import tech.powerjob.common.exception.PowerJobException; +import tech.powerjob.common.response.ResultDTO; /** * 统一处理 web 层异常信息 @@ -25,8 +25,13 @@ public class ControllerExceptionHandler { @ExceptionHandler(Exception.class) public ResultDTO exceptionHandler(Exception e) { + ResultDTO ret = ResultDTO.failed(ExceptionUtils.getMessage(e)); + // 不是所有异常都需要打印完整堆栈,后续可以定义内部的Exception,便于判断 - if (e instanceof IllegalArgumentException || e instanceof PowerJobException) { + if (e instanceof PowerJobException) { + ret.setCode(((PowerJobException) e).getCode()); + log.warn("[ControllerException] PowerJobException, message is {}.", ExceptionUtils.getMessage(e)); + } else if (e instanceof IllegalArgumentException) { log.warn("[ControllerException] http request failed, message is {}.", e.getMessage()); } else if (e instanceof HttpMessageNotReadableException || e instanceof MethodArgumentTypeMismatchException) { log.warn("[ControllerException] invalid http request params, exception is {}.", e.getMessage()); @@ -35,6 +40,7 @@ public class ControllerExceptionHandler { } else { log.error("[ControllerException] http request failed.", e); } - return ResultDTO.failed(ExceptionUtils.getMessage(e)); + + return ret; } } diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/AuthController.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/AuthController.java index 702fdfd9..f7597eeb 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/AuthController.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/AuthController.java @@ -1,18 +1,26 @@ package tech.powerjob.server.web.controller; +import com.google.common.collect.Maps; import org.springframework.web.bind.annotation.*; import tech.powerjob.common.response.ResultDTO; -import tech.powerjob.server.auth.PowerJobUser; +import tech.powerjob.common.serialize.JsonUtils; +import tech.powerjob.server.auth.*; import tech.powerjob.server.auth.common.AuthConstants; +import tech.powerjob.server.auth.dp.GrantDynamicPermission; +import tech.powerjob.server.auth.interceptor.ApiPermission; import tech.powerjob.server.auth.login.LoginTypeInfo; import tech.powerjob.server.auth.service.login.LoginRequest; import tech.powerjob.server.auth.service.login.PowerJobLoginService; +import tech.powerjob.server.auth.service.permission.PowerJobPermissionService; +import tech.powerjob.server.web.request.GrantPermissionRequest; import javax.annotation.Resource; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Optional; /** @@ -27,6 +35,8 @@ public class AuthController { @Resource private PowerJobLoginService powerJobLoginService; + @Resource + private PowerJobPermissionService powerJobPermissionService; @GetMapping("/supportLoginTypes") public ResultDTO> listSupportLoginTypes() { @@ -90,4 +100,34 @@ public class AuthController { private void fillJwt4LoginUser(PowerJobUser powerJobUser, HttpServletResponse httpServletResponse) { httpServletResponse.addCookie(new Cookie(AuthConstants.JWT_NAME, powerJobUser.getJwtToken())); } + + /* 授权相关 */ + @PostMapping("/grantApp") + @ApiPermission(name = "Auth-GrantAppPermission", roleScope = RoleScope.APP, dynamicPermissionPlugin = GrantDynamicPermission.class) + public ResultDTO grantAppPermission(GrantPermissionRequest grantPermissionRequest) { + grantPermission(RoleScope.APP, grantPermissionRequest); + return ResultDTO.success(null); + } + + @PostMapping("/grantNamespace") + @ApiPermission(name = "Auth-GrantNamespacePermission", roleScope = RoleScope.NAMESPACE, dynamicPermissionPlugin = GrantDynamicPermission.class) + public ResultDTO grantNamespacePermission(GrantPermissionRequest grantPermissionRequest) { + grantPermission(RoleScope.NAMESPACE, grantPermissionRequest); + return ResultDTO.success(null); + } + + private void grantPermission(RoleScope roleScope, GrantPermissionRequest grantPermissionRequest) { + + Role role = Role.of(grantPermissionRequest.getRole()); + + Optional.ofNullable(grantPermissionRequest.getUserIds()).orElse(Collections.emptyList()).forEach(uid -> { + // 记录授权人信息 + Map extraInfo = Maps.newHashMap(); + extraInfo.put("grantor", LoginUserHolder.getUserName()); + String extra = JsonUtils.toJSONString(extraInfo); + + powerJobPermissionService.grantPermission(roleScope, grantPermissionRequest.getTargetId(), uid, role, extra); + }); + + } } diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/NamespaceController.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/NamespaceController.java index 456d3471..5aad80f5 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/NamespaceController.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/NamespaceController.java @@ -1,25 +1,43 @@ package tech.powerjob.server.web.controller; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; import org.springframework.web.bind.annotation.*; import tech.powerjob.common.response.ResultDTO; +import tech.powerjob.server.auth.LoginUserHolder; +import tech.powerjob.server.auth.Permission; +import tech.powerjob.server.auth.Role; import tech.powerjob.server.auth.RoleScope; +import tech.powerjob.server.auth.dp.ModifyOrCreateDynamicPermission; +import tech.powerjob.server.auth.gp.SaveNamespaceGrantPermissionPlugin; import tech.powerjob.server.auth.interceptor.ApiPermission; -import tech.powerjob.server.auth.interceptor.dp.ModifyOrCreateDynamicPermission; -import tech.powerjob.server.auth.interceptor.gp.SaveNamespaceGrantPermissionPlugin; import tech.powerjob.server.common.constants.SwitchableStatus; +import tech.powerjob.server.persistence.PageResult; +import tech.powerjob.server.persistence.QueryConvertUtils; import tech.powerjob.server.persistence.remote.model.NamespaceDO; +import tech.powerjob.server.persistence.remote.model.UserInfoDO; +import tech.powerjob.server.persistence.remote.model.UserRoleDO; import tech.powerjob.server.persistence.remote.repository.NamespaceRepository; +import tech.powerjob.server.persistence.remote.repository.UserInfoRepository; +import tech.powerjob.server.persistence.remote.repository.UserRoleRepository; import tech.powerjob.server.web.converter.NamespaceConverter; +import tech.powerjob.server.web.converter.UserConverter; import tech.powerjob.server.web.request.ModifyNamespaceRequest; import tech.powerjob.server.web.request.QueryNamespaceRequest; import tech.powerjob.server.web.response.NamespaceBaseVO; +import tech.powerjob.server.web.response.NamespaceDetailVO; +import tech.powerjob.server.web.response.UserBaseVO; import javax.annotation.Resource; -import java.util.Date; -import java.util.List; -import java.util.Optional; -import java.util.UUID; +import javax.persistence.criteria.Predicate; +import java.util.*; import java.util.stream.Collectors; /** @@ -35,6 +53,10 @@ public class NamespaceController { @Resource private NamespaceRepository namespaceRepository; + @Resource + private UserInfoRepository userInfoRepository; + @Resource + private UserRoleRepository userRoleRepository; @ResponseBody @PostMapping("/save") @@ -53,9 +75,11 @@ public class NamespaceController { namespaceDO.setCode(req.getCode()); // 创建时生成 token namespaceDO.setToken(UUID.randomUUID().toString()); + namespaceDO.setCreator(LoginUserHolder.getUserName()); } else { namespaceDO = fetchById(id); + namespaceDO.setModifier(LoginUserHolder.getUserName()); } // 拷贝通用变更属性(code 不允许更改) @@ -69,9 +93,69 @@ public class NamespaceController { } @PostMapping("/list") - public ResultDTO> listNamespace(@RequestBody QueryNamespaceRequest queryNamespaceRequest) { - List allDos = namespaceRepository.findAll(); - return ResultDTO.success(allDos.stream().map(NamespaceConverter::do2BaseVo).collect(Collectors.toList())); + public ResultDTO> listNamespace(@RequestBody QueryNamespaceRequest queryNamespaceRequest) { + + String codeLike = queryNamespaceRequest.getCodeLike(); + String nameLike = queryNamespaceRequest.getNameLike(); + String tagLike = queryNamespaceRequest.getTagLike(); + + Pageable pageable = PageRequest.of(queryNamespaceRequest.getIndex(), queryNamespaceRequest.getPageSize()); + Specification specification = (root, query, cb) -> { + + List predicates = Lists.newArrayList(); + + if (StringUtils.isNotEmpty(codeLike)) { + predicates.add(cb.like(root.get("code"), QueryConvertUtils.convertLikeParams(codeLike))); + } + + if (StringUtils.isNotEmpty(nameLike)) { + predicates.add(cb.like(root.get("name"), QueryConvertUtils.convertLikeParams(nameLike))); + } + if (StringUtils.isNotEmpty(tagLike)) { + predicates.add(cb.like(root.get("tags"), QueryConvertUtils.convertLikeParams(tagLike))); + } + + if (predicates.isEmpty()) { + return null; + } + return query.where(predicates.toArray(new Predicate[0])).getRestriction(); + }; + + Page namespacePageResult = namespaceRepository.findAll(specification, pageable); + + PageResult ret = new PageResult<>(namespacePageResult); + ret.setData(namespacePageResult.get().map(NamespaceConverter::do2BaseVo).collect(Collectors.toList())); + + return ResultDTO.success(ret); + } + + @GetMapping("/detail") + @ApiPermission(name = "Namespace-GetDetail", roleScope = RoleScope.NAMESPACE, requiredPermission = Permission.READ) + public ResultDTO queryNamespaceDetail(Long id) { + + NamespaceDO namespaceDO = fetchById(id); + NamespaceDetailVO namespaceDetailVO = new NamespaceDetailVO(); + + // 拷贝基础字段 + NamespaceBaseVO namespaceBaseVO = NamespaceConverter.do2BaseVo(namespaceDO); + BeanUtils.copyProperties(namespaceBaseVO, namespaceDetailVO); + + // 处理 token + namespaceDetailVO.setToken(namespaceDO.getToken()); + + // 处理权限视图 + Map> privilegedUsers = Maps.newHashMap(); + namespaceDetailVO.setPrivilegedUsers(privilegedUsers); + List permissionUserList = userRoleRepository.findAllByScopeAndTarget(RoleScope.NAMESPACE.getV(), namespaceDO.getId()); + permissionUserList.forEach(r -> { + Role role = Role.of(r.getRole()); + List userBaseVOList = privilegedUsers.computeIfAbsent(role.name(), ignore -> Lists.newArrayList()); + + Optional userInfoDoOpt = userInfoRepository.findById(r.getUserId()); + userInfoDoOpt.ifPresent(userInfoDO -> userBaseVOList.add(UserConverter.do2BaseVo(userInfoDO))); + }); + + return ResultDTO.success(namespaceDetailVO); } private NamespaceDO fetchById(Long id) { diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/UserInfoController.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/UserInfoController.java index 029c653f..af02f66b 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/UserInfoController.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/controller/UserInfoController.java @@ -1,18 +1,17 @@ package tech.powerjob.server.web.controller; -import tech.powerjob.common.response.ResultDTO; +import com.google.common.collect.Lists; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; +import org.springframework.util.CollectionUtils; +import org.springframework.web.bind.annotation.*; +import tech.powerjob.common.response.ResultDTO; +import tech.powerjob.server.core.service.UserService; import tech.powerjob.server.persistence.remote.model.UserInfoDO; import tech.powerjob.server.persistence.remote.repository.UserInfoRepository; -import tech.powerjob.server.core.service.UserService; +import tech.powerjob.server.web.converter.UserConverter; import tech.powerjob.server.web.request.ModifyUserInfoRequest; -import com.google.common.collect.Lists; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.springframework.util.CollectionUtils; -import org.apache.commons.lang3.StringUtils; -import org.springframework.web.bind.annotation.*; +import tech.powerjob.server.web.response.UserBaseVO; import javax.annotation.Resource; import java.util.List; @@ -27,7 +26,6 @@ import java.util.stream.Collectors; @RestController @RequestMapping("/user") public class UserInfoController { - @Resource private UserService userService; @Resource @@ -42,7 +40,7 @@ public class UserInfoController { } @GetMapping("list") - public ResultDTO> list(@RequestParam(required = false) String name) { + public ResultDTO> list(@RequestParam(required = false) String name) { List result; if (StringUtils.isEmpty(name)) { @@ -53,18 +51,10 @@ public class UserInfoController { return ResultDTO.success(convert(result)); } - private static List convert(List data) { + private static List convert(List data) { if (CollectionUtils.isEmpty(data)) { return Lists.newLinkedList(); } - return data.stream().map(x -> new UserItemVO(x.getId(), x.getUsername())).collect(Collectors.toList()); - } - - @Getter - @NoArgsConstructor - @AllArgsConstructor - public static final class UserItemVO { - private Long id; - private String username; + return data.stream().map(UserConverter::do2BaseVo).collect(Collectors.toList()); } } diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/converter/UserConverter.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/converter/UserConverter.java new file mode 100644 index 00000000..74deec7c --- /dev/null +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/converter/UserConverter.java @@ -0,0 +1,22 @@ +package tech.powerjob.server.web.converter; + +import tech.powerjob.server.persistence.remote.model.UserInfoDO; +import tech.powerjob.server.web.response.UserBaseVO; + +/** + * UserConverter + * + * @author tjq + * @since 2023/9/4 + */ +public class UserConverter { + + public static UserBaseVO do2BaseVo(UserInfoDO x) { + UserBaseVO userBaseVO = new UserBaseVO(); + userBaseVO.setId(x.getId()); + userBaseVO.setUsername(x.getUsername()); + userBaseVO.setNick(x.getNick()); + return userBaseVO; + } + +} diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/request/GrantPermissionRequest.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/request/GrantPermissionRequest.java new file mode 100644 index 00000000..cc2b71d6 --- /dev/null +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/request/GrantPermissionRequest.java @@ -0,0 +1,31 @@ +package tech.powerjob.server.web.request; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 授权请求 + * + * @author tjq + * @since 2024/2/12 + */ +@Data +public class GrantPermissionRequest implements Serializable { + + /** + * 目标ID + */ + private Long targetId; + + /** + * 授予的角色 + */ + private Integer role; + + /** + * 授予的用户IDS + */ + private List userIds; +} diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/request/ModifyNamespaceRequest.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/request/ModifyNamespaceRequest.java index 1d614fb8..06bf052e 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/request/ModifyNamespaceRequest.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/request/ModifyNamespaceRequest.java @@ -26,6 +26,8 @@ public class ModifyNamespaceRequest { */ private String name; + private String dept; + /** * 标签,扩展性之王,多值逗号分割 */ diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/request/QueryAppInfoRequest.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/request/QueryAppInfoRequest.java index 625d2459..28ca91d9 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/request/QueryAppInfoRequest.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/request/QueryAppInfoRequest.java @@ -29,6 +29,7 @@ public class QueryAppInfoRequest { */ private Boolean showMyRelated; + /* ****************** 分页参数 ****************** */ /** * 当前页码 */ diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/request/QueryNamespaceRequest.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/request/QueryNamespaceRequest.java index a47d6015..57f4d8f3 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/request/QueryNamespaceRequest.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/request/QueryNamespaceRequest.java @@ -14,10 +14,22 @@ public class QueryNamespaceRequest { /** * code 模糊查询 */ - private String code; + private String codeLike; /** * 名称模糊查询 */ - private String name; + private String nameLike; + + private String tagLike; + + /* ****************** 分页参数 ****************** */ + /** + * 当前页码 + */ + private Integer index = 0; + /** + * 页大小 + */ + private Integer pageSize = 10; } diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/NamespaceBaseVO.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/NamespaceBaseVO.java index b0e85d75..5df3c640 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/NamespaceBaseVO.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/NamespaceBaseVO.java @@ -30,6 +30,9 @@ public class NamespaceBaseVO implements Serializable { */ private String name; + private String dept; + private String tags; + private Integer status; private String statusStr; diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/NamespaceDetailVO.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/NamespaceDetailVO.java new file mode 100644 index 00000000..2a2eb96f --- /dev/null +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/NamespaceDetailVO.java @@ -0,0 +1,29 @@ +package tech.powerjob.server.web.response; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.util.List; +import java.util.Map; + +/** + * 详细命名空间信息,需要权限访问 + * + * @author tjq + * @since 2023/9/3 + */ +@Getter +@Setter +@ToString(callSuper = true) +public class NamespaceDetailVO extends NamespaceBaseVO { + + /** + * 访问 token + */ + private String token; + /** + * 有权限的用户 + */ + private Map> privilegedUsers; +} diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/UserBaseVO.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/UserBaseVO.java new file mode 100644 index 00000000..c96458b5 --- /dev/null +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/web/response/UserBaseVO.java @@ -0,0 +1,20 @@ +package tech.powerjob.server.web.response; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * 用户基础信息 + * + * @author tjq + * @since 2023/9/3 + */ +@Getter +@Setter +@NoArgsConstructor +public class UserBaseVO { + private Long id; + private String username; + private String nick; +} \ No newline at end of file