diff --git a/src/main/java/me/qwq/doghouse/controller/admin/ConfigController.java b/src/main/java/me/qwq/doghouse/controller/admin/ConfigController.java
index 81e63ec..f084abc 100644
--- a/src/main/java/me/qwq/doghouse/controller/admin/ConfigController.java
+++ b/src/main/java/me/qwq/doghouse/controller/admin/ConfigController.java
@@ -1,7 +1,9 @@
package me.qwq.doghouse.controller.admin;
-import com.alibaba.fastjson2.JSONObject;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.validation.UnexpectedTypeException;
import lombok.extern.slf4j.Slf4j;
import me.qwq.doghouse.annotation.ConfigInfo;
import me.qwq.doghouse.controller.common.BaseController;
@@ -10,10 +12,20 @@ import me.qwq.doghouse.interfaces.IConfig;
import me.qwq.doghouse.pojo.dto.R;
import me.qwq.doghouse.service.ConfigService;
-import org.reflections.Reflections;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Controller;
+import org.springframework.validation.BeanPropertyBindingResult;
+import org.springframework.validation.Validator;
+import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.BindingResult;
/**
@@ -26,10 +38,10 @@ import org.springframework.web.bind.annotation.*;
public class ConfigController extends BaseController {
@Autowired
- ConfigService configService;
+ Validator validator;
@Autowired
- Reflections reflections;
+ ConfigService configService;
/**
* 前端设置页面接口,其设置项必须实现 ConfigInterface 接口,且 resources/templates/admin/config 下有对应的 html 模板,例:
@@ -57,20 +69,52 @@ public class ConfigController extends BaseController {
/**
* 保存配置项统一接口
+ *
+ * 注意该接口和系统初始化配置接口不一样,虽然从 json 中恢复配置,
+ * 但只会替换 json 中指明的字段的值, 其他值不会进行置换
+ *
+ * @param configField 注解 @ConfigInfo 的 field
+ * @param config 配置 json
*/
@PostMapping({"/{configField}", "/{configField}/", "/{configField}/index"})
@ResponseBody
- public > R> saveConfig(@PathVariable String configField, @RequestBody JSONObject config) {
+ public > R> saveConfig(@PathVariable String configField, @RequestBody JsonNode config) throws Exception {
Class clazz = configService.getConfigClassByField(configField);
if (clazz == null) {
throw new PageNotFoundException();
}
- return R.judge(
- configService.saveOrUpdate((T)config.to(clazz)),
- "保存失败");
+ return R.judgeThrow(() -> {
+ // 找出其中有的字段
+ Set jsonFields = new HashSet<>();
+ config.fieldNames().forEachRemaining(jsonFields::add);
+
+ T newConfig = (T)new ObjectMapper().treeToValue(config, clazz);
+ T oldConfig = configService.getConfig(clazz);
+ // 把除了本次更新以外的字段复制过去
+ BeanUtils.copyProperties(oldConfig, newConfig, ArrayUtils.toStringArray(jsonFields.toArray()));
+
+ Method method = this.getClass().getMethod("saveConfig", String.class, JsonNode.class);
+ MethodParameter methodParameter = new MethodParameter(method, 1);
+
+ if (validator.supports(clazz)) {
+ try {
+ BindingResult bindingResult = new BeanPropertyBindingResult(newConfig, clazz.getSimpleName());
+ validator.validate(newConfig, bindingResult);
+
+ if (bindingResult.hasErrors()) {
+ throw new MethodArgumentNotValidException(methodParameter, bindingResult);
+ }
+ }
+ catch (UnexpectedTypeException e) {
+ // 对指定类型未找到 validator,忽略之
+ log.debug("Cannot find a validator for {}", clazz.getName());
+ }
+ }
+
+ return newConfig.saveOrUpdate();
+ });
}
-
}
diff --git a/src/main/java/me/qwq/doghouse/entity/config/MailConfig.java b/src/main/java/me/qwq/doghouse/entity/config/MailConfig.java
index d130c9d..78f5b2a 100644
--- a/src/main/java/me/qwq/doghouse/entity/config/MailConfig.java
+++ b/src/main/java/me/qwq/doghouse/entity/config/MailConfig.java
@@ -74,8 +74,9 @@ public class MailConfig implements Serializable, IConfig {
}
@Override
- public void afterSaving() {
+ public MailConfig afterSaving() {
SpringContextHolder.getBean(MailService.class).resetTaskDelay();
+ return this;
}
}
diff --git a/src/main/java/me/qwq/doghouse/entity/config/MarkdownConfig.java b/src/main/java/me/qwq/doghouse/entity/config/MarkdownConfig.java
index cd0a991..33d71fe 100644
--- a/src/main/java/me/qwq/doghouse/entity/config/MarkdownConfig.java
+++ b/src/main/java/me/qwq/doghouse/entity/config/MarkdownConfig.java
@@ -24,7 +24,7 @@ public class MarkdownConfig implements Serializable, IConfig {
private String mermaidConfig = DEFAULT_MERMAID_CONFIG;
- public void afterSaving() {
+ public MarkdownConfig afterSaving() {
if (StringUtils.isNotBlank(mermaidConfig)) {
try {
JSONObject jo = JSONObject.parse(mermaidConfig);
@@ -33,6 +33,7 @@ public class MarkdownConfig implements Serializable, IConfig {
log.warn("Save mermaid self-defined css failed", e);
}
}
+ return this;
}
}
diff --git a/src/main/java/me/qwq/doghouse/entity/config/NetworkConfig.java b/src/main/java/me/qwq/doghouse/entity/config/NetworkConfig.java
index c3fe925..8f97af6 100644
--- a/src/main/java/me/qwq/doghouse/entity/config/NetworkConfig.java
+++ b/src/main/java/me/qwq/doghouse/entity/config/NetworkConfig.java
@@ -121,8 +121,9 @@ public class NetworkConfig implements Serializable, IConfig {
return trustedCidrBlockList;
}
- public void afterSaving() {
+ public NetworkConfig afterSaving() {
trustedIpList = null;
trustedCidrBlockList = null;
+ return this;
}
}
diff --git a/src/main/java/me/qwq/doghouse/entity/config/PageConfig.java b/src/main/java/me/qwq/doghouse/entity/config/PageConfig.java
index 77b16aa..ad611ce 100644
--- a/src/main/java/me/qwq/doghouse/entity/config/PageConfig.java
+++ b/src/main/java/me/qwq/doghouse/entity/config/PageConfig.java
@@ -65,11 +65,12 @@ public class PageConfig implements Serializable, IConfig, IPostTypeC
private Boolean imageCompress = false;
@Override
- public void afterSaving() {
+ public PageConfig afterSaving() {
// 缓存驱逐:GET_JUMP_TO_URL
// 因为具体是哪一页是和每页评论数有关的,每页评论数又在 SiteConfig 里定义
// 一旦更改,那么评论对应页的缓存得驱逐
((CacheManager)SpringContextHolder.getBean("cacheManager")).getCache(CacheConstants.Comments.GET_JUMP_TO_URL).clear();
+ return this;
}
public PostTypeEnum getPostType() {
diff --git a/src/main/java/me/qwq/doghouse/entity/config/SiteConfig.java b/src/main/java/me/qwq/doghouse/entity/config/SiteConfig.java
index c28da58..41d26d7 100644
--- a/src/main/java/me/qwq/doghouse/entity/config/SiteConfig.java
+++ b/src/main/java/me/qwq/doghouse/entity/config/SiteConfig.java
@@ -103,11 +103,12 @@ public class SiteConfig implements Serializable, IConfig, IPostTypeC
private Boolean imageCompress = false;
@Override
- public void afterSaving() {
+ public SiteConfig afterSaving() {
// 缓存驱逐:GET_JUMP_TO_URL
// 因为具体是哪一页是和每页评论数有关的,每页评论数又在 SiteConfig 里定义
// 一旦更改,那么评论对应页的缓存得驱逐
((CacheManager)SpringContextHolder.getBean("cacheManager")).getCache(CacheConstants.Comments.GET_JUMP_TO_URL).clear();
+ return this;
}
public PostTypeEnum getPostType() {
diff --git a/src/main/java/me/qwq/doghouse/flexmark/ext/mermaid/MermaidNodeRenderer.java b/src/main/java/me/qwq/doghouse/flexmark/ext/mermaid/MermaidNodeRenderer.java
index a8016ca..952ead1 100644
--- a/src/main/java/me/qwq/doghouse/flexmark/ext/mermaid/MermaidNodeRenderer.java
+++ b/src/main/java/me/qwq/doghouse/flexmark/ext/mermaid/MermaidNodeRenderer.java
@@ -153,7 +153,7 @@ public class MermaidNodeRenderer implements NodeRenderer {
if (theme.isInternal()) {
// 内部文件要释放到临时目录使用
- Resource configResource = RESOURCE_LOADER.getResource(theme.getTemplateString("conf/mermaid/config.json"));
+ Resource configResource = RESOURCE_LOADER.getResource("classpath:templates/" + theme.getTemplateString("conf/mermaid/config.json"));
if (configResource.exists()) {
configPath = tmpPath.resolve("config.json");
try (InputStream in = configResource.getInputStream()) {
@@ -169,10 +169,12 @@ public class MermaidNodeRenderer implements NodeRenderer {
configPath = Paths.get(themeService.getCurrentTheme().getDirectory(), "conf/mermaid/config.json");
}
- File configFile = configPath.toFile();
- if (configFile.exists()) {
- cmd.add("-c");
- cmd.add(configPath.toString());
+ if (configPath != null) {
+ File configFile = configPath.toFile();
+ if (configFile.exists()) {
+ cmd.add("-c");
+ cmd.add(configPath.toString());
+ }
}
try {
diff --git a/src/main/java/me/qwq/doghouse/interfaces/IConfig.java b/src/main/java/me/qwq/doghouse/interfaces/IConfig.java
index fb210ce..f4edcda 100644
--- a/src/main/java/me/qwq/doghouse/interfaces/IConfig.java
+++ b/src/main/java/me/qwq/doghouse/interfaces/IConfig.java
@@ -18,8 +18,13 @@ public interface IConfig> {
return SpringContextHolder.getBean(ConfigService.class).saveOrUpdate((T)this);
}
- public default void afterSaving() {}
-
- public default void beforeSaving() {}
+ @SuppressWarnings("unchecked")
+ public default T afterSaving() {
+ return (T) this;
+ }
+ @SuppressWarnings("unchecked")
+ public default T beforeSaving() {
+ return (T) this;
+ }
}
diff --git a/src/main/java/me/qwq/doghouse/service/post/AbstractPostService.java b/src/main/java/me/qwq/doghouse/service/post/AbstractPostService.java
index dab248a..6fa11a3 100644
--- a/src/main/java/me/qwq/doghouse/service/post/AbstractPostService.java
+++ b/src/main/java/me/qwq/doghouse/service/post/AbstractPostService.java
@@ -510,6 +510,9 @@ public abstract class AbstractPostService, C
@UpdatePosts(postViews = true, commentsCount = true)
public PageQuery conditionPage(PageQuery pageQueryCondition) {
PageQuery result = proxy().cacheableConditionPage(pageQueryCondition, isLogin());
+ // result 可能是缓存的,title 又可能是和 siteConfig... 有关的
+ // 获取到结果以后要把与查询无关的内容更新上去
+ result.setTitle(pageQueryCondition.getTitle());
return result;
}