feat: support test mode

This commit is contained in:
tjq 2024-12-08 00:58:30 +08:00
parent 5d82b7cc5c
commit a6de80220a
10 changed files with 352 additions and 151 deletions

View File

@ -64,6 +64,10 @@ public class PowerJobDKey {
public static final String WORKER_RUNTIME_SWAP_TASK_SCHEDULE_INTERVAL_MS = "powerjob.worker.swap.scan-interval"; public static final String WORKER_RUNTIME_SWAP_TASK_SCHEDULE_INTERVAL_MS = "powerjob.worker.swap.scan-interval";
public static final String SERVER_TEST_ACCOUNT_USERNAME = "powerjob.server.test-accounts"; public static final String SERVER_TEST_ACCOUNT_USERNAME = "powerjob.server.test.user.accounts";
public static final String SERVER_TEST_ACCOUNT_PASSWORD = "powerjob.server.test.user.password";
public static final String SERVER_TEST_APP_USERNAME = "powerjob.server.test.app.name";
public static final String SERVER_TEST_APP_PASSWORD = "powerjob.server.test.app.password";
public static final String SERVER_TEST_MODE = "powerjob.server.test.mode";
} }

View File

@ -53,6 +53,10 @@ public enum ErrorCodes {
* 非法参数 * 非法参数
*/ */
ILLEGAL_ARGS_ERROR("-501", "ILLEGAL_ARGS_ERROR"), ILLEGAL_ARGS_ERROR("-501", "ILLEGAL_ARGS_ERROR"),
/**
* 不允许操作
*/
OPERATION_NOT_PERMITTED("-502", "OPERATION_NOT_PERMITTED"),
/** /**
* OPENAPI 错误码号段 -10XX * OPENAPI 错误码号段 -10XX

View File

@ -35,7 +35,7 @@ public class ModifyOrCreateDynamicPermission implements DynamicPermissionPlugin
return Permission.NONE; return Permission.NONE;
} }
return Permission.WRITE; return Permission.SU;
} catch (Exception e) { } catch (Exception e) {
log.error("[ModifyOrCreateDynamicPermission] check permission failed, please fix the bug!!!", e); log.error("[ModifyOrCreateDynamicPermission] check permission failed, please fix the bug!!!", e);
} }

View File

@ -40,16 +40,22 @@ public class NewSystemInitializer implements CommandLineRunner {
@Override @Override
public void run(String... args) throws Exception { public void run(String... args) throws Exception {
initSystemAdmin(); SystemInitializerContext context = new SystemInitializerContext();
initDefaultNamespace(); initSystemAdmin(context);
initDefaultNamespace(context);
initTestEnvironment(context);
} }
private void initSystemAdmin() { private void initSystemAdmin(SystemInitializerContext context) {
clusterInit(SystemInitializeService.GOAL_INIT_ADMIN, Void -> systemInitializeService.initAdmin()); clusterInit(SystemInitializeService.GOAL_INIT_ADMIN, Void -> systemInitializeService.initAdmin(context));
} }
private void initDefaultNamespace() { private void initDefaultNamespace(SystemInitializerContext context) {
clusterInit(SystemInitializeService.GOAL_INIT_NAMESPACE, Void -> systemInitializeService.initNamespace()); clusterInit(SystemInitializeService.GOAL_INIT_NAMESPACE, Void -> systemInitializeService.initNamespace(context));
}
private void initTestEnvironment(SystemInitializerContext context) {
clusterInit(SystemInitializeService.GOAL_INIT_TEST_ENV, Void -> systemInitializeService.initTestEnvironment(context));
} }
private void clusterInit(String name, Consumer<Void> initFunc) { private void clusterInit(String name, Consumer<Void> initFunc) {

View File

@ -10,15 +10,21 @@ public interface SystemInitializeService {
String GOAL_INIT_ADMIN = "goal_init_admin"; String GOAL_INIT_ADMIN = "goal_init_admin";
String GOAL_INIT_NAMESPACE = "goal_init_namespace"; String GOAL_INIT_NAMESPACE = "goal_init_namespace";
String GOAL_INIT_TEST_ENV = "goal_init_test_env";
/** /**
* 初始化超级管理员 * 初始化超级管理员
*/ */
void initAdmin(); void initAdmin(SystemInitializerContext context);
/** /**
* 初始化 namespace * 初始化 namespace
*/ */
void initNamespace(); void initNamespace(SystemInitializerContext context);
/**
* 初始化测试环境
*/
void initTestEnvironment(SystemInitializerContext context);
} }

View File

@ -1,11 +1,13 @@
package tech.powerjob.server.initializer; package tech.powerjob.server.initializer;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import tech.powerjob.common.PowerJobDKey;
import tech.powerjob.common.serialize.JsonUtils; import tech.powerjob.common.serialize.JsonUtils;
import tech.powerjob.server.auth.PowerJobUser; import tech.powerjob.server.auth.PowerJobUser;
import tech.powerjob.server.auth.Role; import tech.powerjob.server.auth.Role;
@ -14,16 +16,23 @@ import tech.powerjob.server.auth.common.AuthConstants;
import tech.powerjob.server.auth.service.login.LoginRequest; import tech.powerjob.server.auth.service.login.LoginRequest;
import tech.powerjob.server.auth.service.login.PowerJobLoginService; import tech.powerjob.server.auth.service.login.PowerJobLoginService;
import tech.powerjob.server.auth.service.permission.PowerJobPermissionService; import tech.powerjob.server.auth.service.permission.PowerJobPermissionService;
import tech.powerjob.server.common.SJ;
import tech.powerjob.server.persistence.remote.model.AppInfoDO;
import tech.powerjob.server.persistence.remote.model.NamespaceDO; import tech.powerjob.server.persistence.remote.model.NamespaceDO;
import tech.powerjob.server.persistence.remote.model.PwjbUserInfoDO; import tech.powerjob.server.persistence.remote.model.PwjbUserInfoDO;
import tech.powerjob.server.web.request.ComponentUserRoleInfo;
import tech.powerjob.server.web.request.ModifyAppInfoRequest;
import tech.powerjob.server.web.request.ModifyNamespaceRequest; import tech.powerjob.server.web.request.ModifyNamespaceRequest;
import tech.powerjob.server.web.request.ModifyUserInfoRequest; import tech.powerjob.server.web.request.ModifyUserInfoRequest;
import tech.powerjob.server.web.service.AppWebService;
import tech.powerjob.server.web.service.NamespaceWebService; import tech.powerjob.server.web.service.NamespaceWebService;
import tech.powerjob.server.web.service.PwjbUserWebService; import tech.powerjob.server.web.service.PwjbUserWebService;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.transaction.Transactional; import javax.transaction.Transactional;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
/** /**
* 初始化 PowerJob 首次部署相关的内容 * 初始化 PowerJob 首次部署相关的内容
@ -39,6 +48,8 @@ public class SystemInitializeServiceImpl implements SystemInitializeService {
@Value("${oms.auth.initiliaze.admin.password:#{null}}") @Value("${oms.auth.initiliaze.admin.password:#{null}}")
private String defaultAdminPassword; private String defaultAdminPassword;
@Resource @Resource
private AppWebService appWebService;
@Resource
private PwjbUserWebService pwjbUserWebService; private PwjbUserWebService pwjbUserWebService;
@Resource @Resource
private NamespaceWebService namespaceWebService; private NamespaceWebService namespaceWebService;
@ -54,36 +65,18 @@ public class SystemInitializeServiceImpl implements SystemInitializeService {
@Override @Override
@Transactional(rollbackOn = Exception.class) @Transactional(rollbackOn = Exception.class)
public void initAdmin() { public void initAdmin(SystemInitializerContext context) {
String username = SYSTEM_ADMIN_NAME; String username = SYSTEM_ADMIN_NAME;
String password = StringUtils.isEmpty(defaultAdminPassword) ? RandomStringUtils.randomAlphabetic(8) : defaultAdminPassword; String password = StringUtils.isEmpty(defaultAdminPassword) ? RandomStringUtils.randomAlphabetic(8) : defaultAdminPassword;
// STEP1: 创建 PWJB 用户 // 创建用户
ModifyUserInfoRequest createUser = new ModifyUserInfoRequest(); PowerJobUser powerJobUser = createUser(username, password, true);
createUser.setUsername(username); context.setAdminUserId(powerJobUser.getId());
createUser.setNick(username);
createUser.setPassword(password);
log.info("[SystemInitializeService] [S1] create default PWJB user by request: {}", createUser); // 授予全局管理员权限
PwjbUserInfoDO savedPwjbUser = pwjbUserWebService.save(createUser);
log.info("[SystemInitializeService] [S1] create default PWJB user successfully: {}", savedPwjbUser);
Map<String, Object> params = Maps.newHashMap();
params.put(AuthConstants.PARAM_KEY_USERNAME, username);
params.put(AuthConstants.PARAM_KEY_PASSWORD, password);
// STEP2: 创建 USER 对象
LoginRequest loginRequest = new LoginRequest()
.setLoginType(AuthConstants.ACCOUNT_TYPE_POWER_JOB)
.setOriginParams(JsonUtils.toJSONString(params));
log.info("[SystemInitializeService] [S2] createPowerJobUser user by request: {}", loginRequest);
PowerJobUser powerJobUser = powerJobLoginService.doLogin(loginRequest);
log.info("[SystemInitializeService] [S2] createPowerJobUser successfully: {}", powerJobUser);
// STEP3: 授予全局管理员权限
powerJobPermissionService.grantRole(RoleScope.GLOBAL, AuthConstants.GLOBAL_ADMIN_TARGET_ID, powerJobUser.getId(), Role.ADMIN, null); powerJobPermissionService.grantRole(RoleScope.GLOBAL, AuthConstants.GLOBAL_ADMIN_TARGET_ID, powerJobUser.getId(), Role.ADMIN, null);
log.info("[SystemInitializeService] [S3] GRANT ADMIN successfully!"); log.info("[SystemInitializeService] GRANT ADMIN to user[{}] successfully!", powerJobUser);
// 循环10遍强提醒用户第一次使用必须更改 admin 密码 // 循环10遍强提醒用户第一次使用必须更改 admin 密码
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
@ -93,13 +86,99 @@ public class SystemInitializeServiceImpl implements SystemInitializeService {
@Override @Override
@Transactional(rollbackOn = Exception.class) @Transactional(rollbackOn = Exception.class)
public void initNamespace() { public void initNamespace(SystemInitializerContext context) {
ModifyNamespaceRequest saveNamespaceReq = new ModifyNamespaceRequest(); ModifyNamespaceRequest saveNamespaceReq = new ModifyNamespaceRequest();
saveNamespaceReq.setName(SYSTEM_DEFAULT_NAMESPACE); saveNamespaceReq.setName(SYSTEM_DEFAULT_NAMESPACE);
saveNamespaceReq.setCode(SYSTEM_DEFAULT_NAMESPACE); saveNamespaceReq.setCode(SYSTEM_DEFAULT_NAMESPACE);
log.info("[SystemInitializeService] create default namespace by request: {}", saveNamespaceReq); log.info("[SystemInitializeService] create default namespace by request: {}", saveNamespaceReq);
NamespaceDO savedNamespaceDO = namespaceWebService.save(saveNamespaceReq); NamespaceDO savedNamespaceDO = namespaceWebService.save(saveNamespaceReq);
context.setDefaultNamespaceId(savedNamespaceDO.getId());
log.info("[SystemInitializeService] create default namespace successfully: {}", savedNamespaceDO); log.info("[SystemInitializeService] create default namespace successfully: {}", savedNamespaceDO);
} }
@Override
@Transactional(rollbackOn = Exception.class)
public void initTestEnvironment(SystemInitializerContext context) {
String testMode = System.getProperty(PowerJobDKey.SERVER_TEST_MODE);
if (!Boolean.TRUE.toString().equalsIgnoreCase(testMode)) {
return;
}
log.warn("[SystemInitializeService] [TestEnv] Detect test mode and start initializing the test environment");
// 初始化测试账号
String testAccountsStr = System.getProperty(PowerJobDKey.SERVER_TEST_ACCOUNT_USERNAME, "powerjob");
String testAccountsPwd = System.getProperty(PowerJobDKey.SERVER_TEST_ACCOUNT_PASSWORD, "powerjob");
List<String> testAccounts = Lists.newArrayList(SJ.COMMA_SPLITTER.split(testAccountsStr));
List<PowerJobUser> testUsers = testAccounts.stream().map(un -> createUser(un, testAccountsPwd, false)).collect(Collectors.toList());
List<Long> testUserIds = testUsers.stream().map(PowerJobUser::getId).collect(Collectors.toList());
log.info("[SystemInitializeService] [TestEnv] test user: {}", testUsers);
String testAppName = System.getProperty(PowerJobDKey.SERVER_TEST_APP_USERNAME, "powerjob-worker-samples");
String testAppPassword = System.getProperty(PowerJobDKey.SERVER_TEST_APP_PASSWORD, "powerjob123");
AppInfoDO testApp = createApp(testAppName, testAppPassword, context.getDefaultNamespaceId(), testUserIds);
log.info("[SystemInitializeService] [TestEnv] test app: {}", testApp);
}
private PowerJobUser createUser(String username, String password, boolean allowedChangePwd) {
// STEP1: 创建 PWJB 用户
ModifyUserInfoRequest createUser = new ModifyUserInfoRequest();
createUser.setUsername(username);
createUser.setNick(username);
createUser.setPassword(password);
if (!allowedChangePwd) {
Map<String, Object> extra = Maps.newHashMap();
extra.put("allowedChangePwd", false);
createUser.setExtra(JsonUtils.toJSONString(extra));
}
log.info("[SystemInitializeService] [username:{}] create PWJB user by request: {}", username, createUser);
PwjbUserInfoDO savedPwjbUser = pwjbUserWebService.save(createUser);
log.info("[SystemInitializeService] [username:{}] create PWJB user successfully: {}", username, savedPwjbUser);
Map<String, Object> params = Maps.newHashMap();
params.put(AuthConstants.PARAM_KEY_USERNAME, username);
params.put(AuthConstants.PARAM_KEY_PASSWORD, password);
// STEP2: 创建 USER 对象
LoginRequest loginRequest = new LoginRequest()
.setLoginType(AuthConstants.ACCOUNT_TYPE_POWER_JOB)
.setOriginParams(JsonUtils.toJSONString(params));
log.info("[SystemInitializeService] [username:{}] create PowerJobUser by request: {}", username, loginRequest);
PowerJobUser powerJobUser = powerJobLoginService.doLogin(loginRequest);
log.info("[SystemInitializeService] [username:{}] create PowerJobUser successfully: {}", username, powerJobUser);
return powerJobUser;
}
private AppInfoDO createApp(String appName, String password, Long namespaceId, List<Long> developers) {
ModifyAppInfoRequest modifyAppInfoRequest = new ModifyAppInfoRequest();
modifyAppInfoRequest.setAppName(appName);
modifyAppInfoRequest.setTitle(appName);
modifyAppInfoRequest.setPassword(password);
modifyAppInfoRequest.setNamespaceId(namespaceId);
modifyAppInfoRequest.setTags("test_app");
// 禁用靠密码成为管理员
Map<String, Object> extra = Maps.newHashMap();
extra.put("allowedBecomeAdminByPassword", false);
modifyAppInfoRequest.setExtra(JsonUtils.toJSONString(extra));
ComponentUserRoleInfo componentUserRoleInfo = new ComponentUserRoleInfo();
modifyAppInfoRequest.setComponentUserRoleInfo(componentUserRoleInfo);
componentUserRoleInfo.setDeveloper(developers);
log.info("[SystemInitializeService] [app:{}] create App by request: {}", appName, modifyAppInfoRequest);
AppInfoDO appInfoDO = appWebService.save(modifyAppInfoRequest);
log.info("[SystemInitializeService] [app:{}] create App successfully: {}", appName, appInfoDO);
return appInfoDO;
}
} }

View File

@ -0,0 +1,21 @@
package tech.powerjob.server.initializer;
import lombok.Data;
import java.util.Map;
/**
* 系统初始化上下文
*
* @author tjq
* @since 2024/12/8
*/
@Data
public class SystemInitializerContext {
private Long adminUserId;
private Long defaultNamespaceId;
private Map<String, Object> extra;
}

View File

@ -2,27 +2,18 @@ package tech.powerjob.server.web.controller;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page; 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.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import tech.powerjob.common.enums.ErrorCodes;
import tech.powerjob.common.exception.PowerJobExceptionLauncher;
import tech.powerjob.common.response.ResultDTO; import tech.powerjob.common.response.ResultDTO;
import tech.powerjob.common.serialize.JsonUtils; import tech.powerjob.common.serialize.JsonUtils;
import tech.powerjob.common.utils.CommonUtils; import tech.powerjob.common.utils.CommonUtils;
import tech.powerjob.server.auth.LoginUserHolder;
import tech.powerjob.server.auth.Permission; import tech.powerjob.server.auth.Permission;
import tech.powerjob.server.auth.Role; import tech.powerjob.server.auth.Role;
import tech.powerjob.server.auth.RoleScope; import tech.powerjob.server.auth.RoleScope;
@ -31,14 +22,10 @@ import tech.powerjob.server.auth.interceptor.ApiPermission;
import tech.powerjob.server.auth.plugin.ModifyOrCreateDynamicPermission; import tech.powerjob.server.auth.plugin.ModifyOrCreateDynamicPermission;
import tech.powerjob.server.auth.plugin.SaveAppGrantPermissionPlugin; import tech.powerjob.server.auth.plugin.SaveAppGrantPermissionPlugin;
import tech.powerjob.server.auth.service.WebAuthService; import tech.powerjob.server.auth.service.WebAuthService;
import tech.powerjob.server.common.module.WorkerInfo;
import tech.powerjob.server.core.service.AppInfoService; import tech.powerjob.server.core.service.AppInfoService;
import tech.powerjob.server.persistence.PageResult; import tech.powerjob.server.persistence.PageResult;
import tech.powerjob.server.persistence.QueryConvertUtils;
import tech.powerjob.server.persistence.remote.model.AppInfoDO; import tech.powerjob.server.persistence.remote.model.AppInfoDO;
import tech.powerjob.server.persistence.remote.model.NamespaceDO; import tech.powerjob.server.persistence.remote.model.NamespaceDO;
import tech.powerjob.server.persistence.remote.repository.AppInfoRepository;
import tech.powerjob.server.remote.worker.WorkerClusterQueryService;
import tech.powerjob.server.web.converter.NamespaceConverter; import tech.powerjob.server.web.converter.NamespaceConverter;
import tech.powerjob.server.web.request.AppAssertRequest; import tech.powerjob.server.web.request.AppAssertRequest;
import tech.powerjob.server.web.request.ComponentUserRoleInfo; import tech.powerjob.server.web.request.ComponentUserRoleInfo;
@ -47,11 +34,13 @@ import tech.powerjob.server.web.request.QueryAppInfoRequest;
import tech.powerjob.server.web.response.AppInfoVO; import tech.powerjob.server.web.response.AppInfoVO;
import tech.powerjob.server.web.response.NamespaceBaseVO; import tech.powerjob.server.web.response.NamespaceBaseVO;
import tech.powerjob.server.web.response.UserBaseVO; import tech.powerjob.server.web.response.UserBaseVO;
import tech.powerjob.server.web.service.AppWebService;
import tech.powerjob.server.web.service.NamespaceWebService; import tech.powerjob.server.web.service.NamespaceWebService;
import tech.powerjob.server.web.service.UserWebService; import tech.powerjob.server.web.service.UserWebService;
import javax.persistence.criteria.Predicate; import java.util.List;
import java.util.*; import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -67,62 +56,20 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor @RequiredArgsConstructor
public class AppInfoController { public class AppInfoController {
private final AppWebService appWebService;
private final WebAuthService webAuthService; private final WebAuthService webAuthService;
private final UserWebService userWebService; private final UserWebService userWebService;
private final AppInfoService appInfoService; private final AppInfoService appInfoService;
private final AppInfoRepository appInfoRepository;
private final NamespaceWebService namespaceWebService; private final NamespaceWebService namespaceWebService;
private final WorkerClusterQueryService workerClusterQueryService;
@PostMapping("/save") @PostMapping("/save")
@ApiPermission(name = "App-Save", roleScope = RoleScope.APP, dynamicPermissionPlugin = ModifyOrCreateDynamicPermission.class, grandPermissionPlugin = SaveAppGrantPermissionPlugin.class) @ApiPermission(name = "App-Save", roleScope = RoleScope.APP, dynamicPermissionPlugin = ModifyOrCreateDynamicPermission.class, grandPermissionPlugin = SaveAppGrantPermissionPlugin.class)
public ResultDTO<AppInfoVO> saveAppInfo(@RequestBody ModifyAppInfoRequest req) { public ResultDTO<AppInfoVO> saveAppInfo(@RequestBody ModifyAppInfoRequest req) {
// 根据 ns code 填充 namespaceId自动化创建过程中固定的 namespace-code 对用户更友好 AppInfoDO savedAppInfo = appWebService.save(req);
if (StringUtils.isNotEmpty(req.getNamespaceCode())) {
namespaceWebService.findByCode(req.getNamespaceCode()).ifPresent(x -> req.setNamespaceId(x.getId()));
}
req.valid();
AppInfoDO appInfoDO;
Long id = req.getId();
if (id == null) {
// 前置校验防止部分没加唯一索引的 DB 重复创建记录导致异常
appInfoRepository.findByAppName(req.getAppName()).ifPresent(x -> new PowerJobExceptionLauncher(ErrorCodes.ILLEGAL_ARGS_ERROR, String.format("App[%s] already exists", req.getAppName())));
appInfoDO = new AppInfoDO();
appInfoDO.setGmtCreate(new Date());
appInfoDO.setCreator(LoginUserHolder.getUserId());
} else {
appInfoDO = appInfoService.findById(id, false).orElseThrow(() -> new IllegalArgumentException("can't find appInfo by id:" + id));
// 不允许修改 appName
if (!appInfoDO.getAppName().equalsIgnoreCase(req.getAppName())) {
throw new IllegalArgumentException("NOT_ALLOW_CHANGE_THE_APP_NAME");
}
}
appInfoDO.setAppName(req.getAppName());
appInfoDO.setTitle(req.getTitle());
appInfoDO.setPassword(req.getPassword());
appInfoDO.setNamespaceId(req.getNamespaceId());
appInfoDO.setTags(req.getTags());
appInfoDO.setExtra(req.getExtra());
appInfoDO.setGmtModified(new Date());
appInfoDO.setModifier(LoginUserHolder.getUserId());
AppInfoDO savedAppInfo = appInfoService.save(appInfoDO);
// 重现授权
webAuthService.processPermissionOnSave(RoleScope.APP, savedAppInfo.getId(), req.getComponentUserRoleInfo());
return ResultDTO.success(convert(Lists.newArrayList(savedAppInfo), false).get(0)); return ResultDTO.success(convert(Lists.newArrayList(savedAppInfo), false).get(0));
} }
@ -131,15 +78,7 @@ public class AppInfoController {
@ApiPermission(name = "App-Delete", roleScope = RoleScope.APP, requiredPermission = Permission.SU) @ApiPermission(name = "App-Delete", roleScope = RoleScope.APP, requiredPermission = Permission.SU)
public ResultDTO<Void> deleteApp(Long appId) { public ResultDTO<Void> deleteApp(Long appId) {
log.warn("[AppInfoController] try to delete app: {}", appId); appWebService.delete(appId);
List<WorkerInfo> allAliveWorkers = workerClusterQueryService.getAllAliveWorkers(appId);
if (CollectionUtils.isNotEmpty(allAliveWorkers)) {
return ResultDTO.failed("Unable to delete apps with live workers, Please remove the worker dependency first!");
}
appInfoService.deleteById(appId);
log.warn("[AppInfoController] delete app[id={}] successfully!", appId);
return ResultDTO.success(null); return ResultDTO.success(null);
} }
@ -147,54 +86,7 @@ public class AppInfoController {
@ApiPermission(name = "App-List", roleScope = RoleScope.APP, requiredPermission = Permission.NONE) @ApiPermission(name = "App-List", roleScope = RoleScope.APP, requiredPermission = Permission.NONE)
public ResultDTO<PageResult<AppInfoVO>> listAppInfoByQuery(@RequestBody QueryAppInfoRequest queryAppInfoRequest) { public ResultDTO<PageResult<AppInfoVO>> listAppInfoByQuery(@RequestBody QueryAppInfoRequest queryAppInfoRequest) {
Pageable pageable = PageRequest.of(queryAppInfoRequest.getIndex(), queryAppInfoRequest.getPageSize()); Page<AppInfoDO> pageAppInfoResult = appWebService.list(queryAppInfoRequest);
// 相关权限先查处关联 ids
Set<Long> queryAppIds;
Boolean showMyRelated = queryAppInfoRequest.getShowMyRelated();
if (BooleanUtils.isTrue(showMyRelated)) {
Set<Long> targetIds = Sets.newHashSet();
webAuthService.fetchMyPermissionTargets(RoleScope.APP).values().forEach(targetIds::addAll);
queryAppIds = targetIds;
if (CollectionUtils.isEmpty(queryAppIds)) {
return ResultDTO.success(new PageResult<>());
}
} else {
queryAppIds = Collections.emptySet();
}
Specification<AppInfoDO> specification = (root, query, criteriaBuilder) -> {
List<Predicate> predicates = Lists.newArrayList();
Long appId = queryAppInfoRequest.getAppId();
Long namespaceId = queryAppInfoRequest.getNamespaceId();
if (appId != null) {
predicates.add(criteriaBuilder.equal(root.get("id"), appId));
}
if (namespaceId != null) {
predicates.add(criteriaBuilder.equal(root.get("namespaceId"), namespaceId));
}
if (StringUtils.isNotEmpty(queryAppInfoRequest.getAppNameLike())) {
predicates.add(criteriaBuilder.like(root.get("appName"), QueryConvertUtils.convertLikeParams(queryAppInfoRequest.getAppNameLike())));
}
if (StringUtils.isNotEmpty(queryAppInfoRequest.getTagLike())) {
predicates.add(criteriaBuilder.like(root.get("tags"), QueryConvertUtils.convertLikeParams(queryAppInfoRequest.getTagLike())));
}
if (!queryAppIds.isEmpty()) {
predicates.add(criteriaBuilder.in(root.get("id")).value(queryAppIds));
}
return query.where(predicates.toArray(new Predicate[0])).getRestriction();
};
Page<AppInfoDO> pageAppInfoResult = appInfoRepository.findAll(specification, pageable);
PageResult<AppInfoVO> pageRet = new PageResult<>(pageAppInfoResult); PageResult<AppInfoVO> pageRet = new PageResult<>(pageAppInfoResult);

View File

@ -0,0 +1,21 @@
package tech.powerjob.server.web.service;
import org.springframework.data.domain.Page;
import tech.powerjob.server.persistence.remote.model.AppInfoDO;
import tech.powerjob.server.web.request.ModifyAppInfoRequest;
import tech.powerjob.server.web.request.QueryAppInfoRequest;
/**
* AppWebService
*
* @author tjq
* @since 2024/12/8
*/
public interface AppWebService {
AppInfoDO save(ModifyAppInfoRequest request);
void delete(Long id);
Page<AppInfoDO> list(QueryAppInfoRequest queryAppInfoRequest);
}

View File

@ -0,0 +1,168 @@
package tech.powerjob.server.web.service.impl;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
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.stereotype.Service;
import tech.powerjob.common.enums.ErrorCodes;
import tech.powerjob.common.exception.PowerJobException;
import tech.powerjob.common.exception.PowerJobExceptionLauncher;
import tech.powerjob.server.auth.LoginUserHolder;
import tech.powerjob.server.auth.RoleScope;
import tech.powerjob.server.auth.service.WebAuthService;
import tech.powerjob.server.common.module.WorkerInfo;
import tech.powerjob.server.core.service.AppInfoService;
import tech.powerjob.server.persistence.QueryConvertUtils;
import tech.powerjob.server.persistence.remote.model.AppInfoDO;
import tech.powerjob.server.persistence.remote.repository.AppInfoRepository;
import tech.powerjob.server.remote.worker.WorkerClusterQueryService;
import tech.powerjob.server.web.request.ModifyAppInfoRequest;
import tech.powerjob.server.web.request.QueryAppInfoRequest;
import tech.powerjob.server.web.service.AppWebService;
import tech.powerjob.server.web.service.NamespaceWebService;
import javax.persistence.criteria.Predicate;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Set;
/**
* AppWebService
*
* @author tjq
* @since 2024/12/8
*/
@Slf4j
@Service
@AllArgsConstructor
public class AppWebServiceImpl implements AppWebService {
private final WebAuthService webAuthService;
private final AppInfoService appInfoService;
private final AppInfoRepository appInfoRepository;
private final NamespaceWebService namespaceWebService;
private final WorkerClusterQueryService workerClusterQueryService;
@Override
public AppInfoDO save(ModifyAppInfoRequest req) {
// 根据 ns code 填充 namespaceId自动化创建过程中固定的 namespace-code 对用户更友好
if (StringUtils.isNotEmpty(req.getNamespaceCode())) {
namespaceWebService.findByCode(req.getNamespaceCode()).ifPresent(x -> req.setNamespaceId(x.getId()));
}
req.valid();
AppInfoDO appInfoDO;
Long id = req.getId();
if (id == null) {
// 前置校验防止部分没加唯一索引的 DB 重复创建记录导致异常
appInfoRepository.findByAppName(req.getAppName()).ifPresent(x -> new PowerJobExceptionLauncher(ErrorCodes.ILLEGAL_ARGS_ERROR, String.format("App[%s] already exists", req.getAppName())));
appInfoDO = new AppInfoDO();
appInfoDO.setGmtCreate(new Date());
appInfoDO.setCreator(LoginUserHolder.getUserId());
} else {
appInfoDO = appInfoService.findById(id, false).orElseThrow(() -> new IllegalArgumentException("can't find appInfo by id:" + id));
// 不允许修改 appName
if (!appInfoDO.getAppName().equalsIgnoreCase(req.getAppName())) {
throw new IllegalArgumentException("NOT_ALLOW_CHANGE_THE_APP_NAME");
}
}
appInfoDO.setAppName(req.getAppName());
appInfoDO.setTitle(req.getTitle());
appInfoDO.setPassword(req.getPassword());
appInfoDO.setNamespaceId(req.getNamespaceId());
appInfoDO.setTags(req.getTags());
appInfoDO.setExtra(req.getExtra());
appInfoDO.setGmtModified(new Date());
appInfoDO.setModifier(LoginUserHolder.getUserId());
AppInfoDO savedAppInfo = appInfoService.save(appInfoDO);
// 重现授权
webAuthService.processPermissionOnSave(RoleScope.APP, savedAppInfo.getId(), req.getComponentUserRoleInfo());
return savedAppInfo;
}
@Override
public void delete(Long appId) {
log.warn("[AppInfoController] try to delete app: {}", appId);
List<WorkerInfo> allAliveWorkers = workerClusterQueryService.getAllAliveWorkers(appId);
if (CollectionUtils.isNotEmpty(allAliveWorkers)) {
throw new PowerJobException(ErrorCodes.OPERATION_NOT_PERMITTED, "Unable to delete apps with live workers, Please remove the worker dependency first!");
}
appInfoService.deleteById(appId);
log.warn("[AppInfoController] delete app[id={}] successfully!", appId);
}
@Override
public Page<AppInfoDO> list(QueryAppInfoRequest queryAppInfoRequest) {
Pageable pageable = PageRequest.of(queryAppInfoRequest.getIndex(), queryAppInfoRequest.getPageSize());
// 相关权限先查处关联 ids
Set<Long> queryAppIds;
Boolean showMyRelated = queryAppInfoRequest.getShowMyRelated();
if (BooleanUtils.isTrue(showMyRelated)) {
Set<Long> targetIds = Sets.newHashSet();
webAuthService.fetchMyPermissionTargets(RoleScope.APP).values().forEach(targetIds::addAll);
queryAppIds = targetIds;
if (CollectionUtils.isEmpty(queryAppIds)) {
return Page.empty();
}
} else {
queryAppIds = Collections.emptySet();
}
Specification<AppInfoDO> specification = (root, query, criteriaBuilder) -> {
List<Predicate> predicates = Lists.newArrayList();
Long appId = queryAppInfoRequest.getAppId();
Long namespaceId = queryAppInfoRequest.getNamespaceId();
if (appId != null) {
predicates.add(criteriaBuilder.equal(root.get("id"), appId));
}
if (namespaceId != null) {
predicates.add(criteriaBuilder.equal(root.get("namespaceId"), namespaceId));
}
if (StringUtils.isNotEmpty(queryAppInfoRequest.getAppNameLike())) {
predicates.add(criteriaBuilder.like(root.get("appName"), QueryConvertUtils.convertLikeParams(queryAppInfoRequest.getAppNameLike())));
}
if (StringUtils.isNotEmpty(queryAppInfoRequest.getTagLike())) {
predicates.add(criteriaBuilder.like(root.get("tags"), QueryConvertUtils.convertLikeParams(queryAppInfoRequest.getTagLike())));
}
if (!queryAppIds.isEmpty()) {
predicates.add(criteriaBuilder.in(root.get("id")).value(queryAppIds));
}
return query.where(predicates.toArray(new Predicate[0])).getRestriction();
};
return appInfoRepository.findAll(specification, pageable);
}
}