diff --git a/powerjob-common/src/main/java/tech/powerjob/common/enums/ErrorCodes.java b/powerjob-common/src/main/java/tech/powerjob/common/enums/ErrorCodes.java
index db0043c6..5b666a32 100644
--- a/powerjob-common/src/main/java/tech/powerjob/common/enums/ErrorCodes.java
+++ b/powerjob-common/src/main/java/tech/powerjob/common/enums/ErrorCodes.java
@@ -13,6 +13,8 @@ import lombok.Getter;
@AllArgsConstructor
public enum ErrorCodes {
+ SYS_ACQUIRE_LOCK_FAILED("-10", "SYS_ACQUIRE_LOCK_FAILED"),
+
USER_NOT_LOGIN("-100", "UserNotLoggedIn"),
USER_NOT_EXIST("-101", "UserNotExist"),
USER_AUTH_FAILED("-102", "UserAuthFailed"),
diff --git a/powerjob-server/pom.xml b/powerjob-server/pom.xml
index f4533d83..8883716e 100644
--- a/powerjob-server/pom.xml
+++ b/powerjob-server/pom.xml
@@ -23,6 +23,7 @@
powerjob-server-core
powerjob-server-monitor
powerjob-server-auth
+ powerjob-server-infrastructure
@@ -87,6 +88,11 @@
powerjob-server-persistence
${project.version}
+
+ tech.powerjob
+ powerjob-server-infrastructure
+ ${project.version}
+
tech.powerjob
powerjob-server-core
diff --git a/powerjob-server/powerjob-server-Infrastructure/pom.xml b/powerjob-server/powerjob-server-Infrastructure/pom.xml
new file mode 100644
index 00000000..b9109421
--- /dev/null
+++ b/powerjob-server/powerjob-server-Infrastructure/pom.xml
@@ -0,0 +1,34 @@
+
+
+
+
+ tech.powerjob
+ powerjob-server
+ 5.1.0
+
+
+ 4.0.0
+
+ powerjob-server-infrastructure
+ ${project.parent.version}
+
+
+ 8
+ 8
+ UTF-8
+
+
+
+
+
+ tech.powerjob
+ powerjob-server-persistence
+ provided
+
+
+
+
+
+
\ No newline at end of file
diff --git a/powerjob-server/powerjob-server-Infrastructure/src/main/java/tech/powerjob/server/infrastructure/config/Config.java b/powerjob-server/powerjob-server-Infrastructure/src/main/java/tech/powerjob/server/infrastructure/config/Config.java
new file mode 100644
index 00000000..148bba9d
--- /dev/null
+++ b/powerjob-server/powerjob-server-Infrastructure/src/main/java/tech/powerjob/server/infrastructure/config/Config.java
@@ -0,0 +1,23 @@
+package tech.powerjob.server.infrastructure.config;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * 配置对象
+ *
+ * @author tjq
+ * @since 2024/8/24
+ */
+@Data
+@Accessors(chain = true)
+public class Config implements Serializable {
+
+ private String key;
+
+ private String value;
+
+ private String comment;
+}
diff --git a/powerjob-server/powerjob-server-Infrastructure/src/main/java/tech/powerjob/server/infrastructure/config/ConfigService.java b/powerjob-server/powerjob-server-Infrastructure/src/main/java/tech/powerjob/server/infrastructure/config/ConfigService.java
new file mode 100644
index 00000000..3b9f40e1
--- /dev/null
+++ b/powerjob-server/powerjob-server-Infrastructure/src/main/java/tech/powerjob/server/infrastructure/config/ConfigService.java
@@ -0,0 +1,18 @@
+package tech.powerjob.server.infrastructure.config;
+
+/**
+ * 配置服务
+ *
+ * @author tjq
+ * @since 2024/8/24
+ */
+public interface ConfigService {
+
+ /**
+ * 获取配置
+ * @param key 配置名称
+ * @param defaultValue 默认值
+ * @return 结果
+ */
+ String fetchConfig(String key, String defaultValue);
+}
diff --git a/powerjob-server/powerjob-server-Infrastructure/src/main/java/tech/powerjob/server/infrastructure/config/DynamicServerConfigCrudService.java b/powerjob-server/powerjob-server-Infrastructure/src/main/java/tech/powerjob/server/infrastructure/config/DynamicServerConfigCrudService.java
new file mode 100644
index 00000000..72e8f4b6
--- /dev/null
+++ b/powerjob-server/powerjob-server-Infrastructure/src/main/java/tech/powerjob/server/infrastructure/config/DynamicServerConfigCrudService.java
@@ -0,0 +1,33 @@
+package tech.powerjob.server.infrastructure.config;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 服务端配置 CRUD 服务
+ *
+ * @author tjq
+ * @since 2024/8/24
+ */
+public interface DynamicServerConfigCrudService {
+
+ /**
+ * 保存配置
+ * @param config 配置信息
+ */
+ void save(Config config);
+
+ Optional fetch(String key);
+
+ /**
+ * 删除配置
+ * @param key
+ */
+ void delete(String key);
+
+ /**
+ * 列出所有配置
+ * @return 配置
+ */
+ List list();
+}
diff --git a/powerjob-server/powerjob-server-Infrastructure/src/main/java/tech/powerjob/server/infrastructure/config/impl/ConfigServiceImpl.java b/powerjob-server/powerjob-server-Infrastructure/src/main/java/tech/powerjob/server/infrastructure/config/impl/ConfigServiceImpl.java
new file mode 100644
index 00000000..ff4be7e9
--- /dev/null
+++ b/powerjob-server/powerjob-server-Infrastructure/src/main/java/tech/powerjob/server/infrastructure/config/impl/ConfigServiceImpl.java
@@ -0,0 +1,36 @@
+package tech.powerjob.server.infrastructure.config.impl;
+
+import org.springframework.core.env.Environment;
+import org.springframework.stereotype.Service;
+import tech.powerjob.server.infrastructure.config.Config;
+import tech.powerjob.server.infrastructure.config.ConfigService;
+import tech.powerjob.server.infrastructure.config.DynamicServerConfigCrudService;
+
+import javax.annotation.Resource;
+import java.util.Optional;
+
+/**
+ * ConfigService
+ *
+ * @author tjq
+ * @since 2024/8/24
+ */
+@Service
+public class ConfigServiceImpl implements ConfigService {
+
+ @Resource
+ private Environment environment;
+ @Resource
+ private DynamicServerConfigCrudService dynamicServerConfigCrudService;
+
+ @Override
+ public String fetchConfig(String key, String defaultValue) {
+
+ Optional configByDbOpt = dynamicServerConfigCrudService.fetch(key);
+ if (configByDbOpt.isPresent()) {
+ return configByDbOpt.get().getValue();
+ }
+
+ return environment.getProperty(key, defaultValue);
+ }
+}
diff --git a/powerjob-server/powerjob-server-Infrastructure/src/main/java/tech/powerjob/server/infrastructure/config/impl/DynamicServerConfigCrudServiceImpl.java b/powerjob-server/powerjob-server-Infrastructure/src/main/java/tech/powerjob/server/infrastructure/config/impl/DynamicServerConfigCrudServiceImpl.java
new file mode 100644
index 00000000..695451cf
--- /dev/null
+++ b/powerjob-server/powerjob-server-Infrastructure/src/main/java/tech/powerjob/server/infrastructure/config/impl/DynamicServerConfigCrudServiceImpl.java
@@ -0,0 +1,105 @@
+package tech.powerjob.server.infrastructure.config.impl;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import tech.powerjob.common.enums.ErrorCodes;
+import tech.powerjob.common.exception.PowerJobException;
+import tech.powerjob.server.extension.LockService;
+import tech.powerjob.server.infrastructure.config.Config;
+import tech.powerjob.server.infrastructure.config.DynamicServerConfigCrudService;
+import tech.powerjob.server.persistence.remote.model.SundryDO;
+import tech.powerjob.server.persistence.remote.repository.SundryRepository;
+
+import javax.annotation.Resource;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * DynamicServerConfigCrudService
+ *
+ * @author tjq
+ * @since 2024/8/24
+ */
+@Slf4j
+@Service
+public class DynamicServerConfigCrudServiceImpl implements DynamicServerConfigCrudService {
+
+ @Resource
+ private LockService lockService;
+ @Resource
+ private SundryRepository sundryRepository;
+
+ private static final String PKEY = "sys.powerjob.server.config";
+
+ private static final int MAX_LOCK_TIME = 60000;
+
+ @Override
+ public void save(Config config) {
+ String lockKey = PKEY.concat(config.getKey());
+ boolean acquiredLock = lockService.tryLock(lockKey, MAX_LOCK_TIME);
+ if (!acquiredLock) {
+ throw new PowerJobException(ErrorCodes.SYS_ACQUIRE_LOCK_FAILED, "请稍后重试");
+ }
+
+ SundryDO sundryDO;
+
+ try {
+ Optional oldConfigOpt = sundryRepository.findByPkeyAndSkey(PKEY, config.getKey());
+ if (oldConfigOpt.isPresent()) {
+ sundryDO = oldConfigOpt.get();
+ fillConfig2SundryDO(sundryDO, config);
+ } else {
+ // 纯新增
+ sundryDO = new SundryDO();
+ fillConfig2SundryDO(sundryDO, config);
+ sundryDO.setGmtCreate(new Date());
+ }
+
+ sundryRepository.saveAndFlush(sundryDO);
+ log.info("[DynamicServerConfigCrudService] save config successfully, config: {}, sundry: {}", config, sundryDO);
+
+ } finally {
+ lockService.unlock(lockKey);
+ }
+ }
+
+ @Override
+ public Optional fetch(String key) {
+ Optional oldConfigOpt = sundryRepository.findByPkeyAndSkey(PKEY, key);
+ return oldConfigOpt.map(DynamicServerConfigCrudServiceImpl::convert2Config);
+ }
+
+ @Override
+ public void delete(String key) {
+ Optional deletedOpt = sundryRepository.deleteByPkeyAndSkey(PKEY, key);
+ if (deletedOpt.isPresent()) {
+ log.info("[DynamicServerConfigCrudService] delete config[{}] successfully, origin data: {}", key, deletedOpt.get());
+ } else {
+ log.warn("[DynamicServerConfigCrudService] config[{}] not exist, no need to delete!", key);
+ }
+ }
+
+ @Override
+ public List list() {
+ List allByPkey = sundryRepository.findAllByPkey(PKEY);
+ return Optional.ofNullable(allByPkey).orElse(Collections.emptyList()).stream().map(DynamicServerConfigCrudServiceImpl::convert2Config).collect(Collectors.toList());
+ }
+
+ private static void fillConfig2SundryDO(SundryDO sundryDO, Config config) {
+ sundryDO.setPkey(PKEY);
+ sundryDO.setSkey(config.getKey());
+ sundryDO.setContent(config.getValue());
+ sundryDO.setExtra(config.getComment());
+ sundryDO.setGmtModified(new Date());
+ }
+
+ private static Config convert2Config(SundryDO sundryDO) {
+ return new Config()
+ .setKey(sundryDO.getSkey())
+ .setValue(sundryDO.getContent())
+ .setComment(sundryDO.getExtra());
+ }
+}
diff --git a/powerjob-server/powerjob-server-Infrastructure/src/main/java/tech/powerjob/server/infrastructure/package-info.java b/powerjob-server/powerjob-server-Infrastructure/src/main/java/tech/powerjob/server/infrastructure/package-info.java
new file mode 100644
index 00000000..c5134df6
--- /dev/null
+++ b/powerjob-server/powerjob-server-Infrastructure/src/main/java/tech/powerjob/server/infrastructure/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * 基础服务层
+ *
+ * @author tjq
+ * @since 2024/8/24
+ */
+package tech.powerjob.server.infrastructure;
\ No newline at end of file
diff --git a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/SundryRepository.java b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/SundryRepository.java
index 62e47a5a..41b70f37 100644
--- a/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/SundryRepository.java
+++ b/powerjob-server/powerjob-server-persistence/src/main/java/tech/powerjob/server/persistence/remote/repository/SundryRepository.java
@@ -17,4 +17,6 @@ public interface SundryRepository extends JpaRepository {
List findAllByPkey(String pkey);
Optional findByPkeyAndSkey(String pkey, String skey);
+
+ Optional deleteByPkeyAndSkey(String pkey, String skey);
}