feat(theme): 皮肤包管理收敛与命名批次-兜底catch经wrapFailure先log.error记原始堆栈再包装(upload/derive/updateMeta三处,extractZip/deleteRecursively静默吞改warn/debug);内置包改显示名走.meta覆盖层(ThemeRegistry加载后覆写title/description,updateMeta放开内置:全空=恢复默认,params拒改);derive/upload支持表单命名(theme.json改写regex换ObjectMapper防误替换,upload落位前改staging失败不落位);resolveTheme回退warn补站点编码;ThemeVo.editable语义改为可编辑元数据;附内置包mint-breeze/fresh-mint2与R1学校主站编码迁移SQL;308/308全绿
build / build-push (push) Successful in 48s
build / build-push (push) Successful in 48s
This commit is contained in:
@@ -50,6 +50,9 @@ public class ThemeRegistry {
|
||||
|
||||
private static final String SCAN_PATTERN = "classpath*:templates/themes/*/theme.json";
|
||||
|
||||
/** 元数据覆盖层目录(任务四):{cms.theme.dir}/.meta/{name}.json;外部包扫描按「含 theme.json 的目录」过滤,天然跳过 */
|
||||
private static final String META_OVERRIDE_DIR = ".meta";
|
||||
|
||||
/** 未知字段忽略、已知字段兼容(方案 §4.2 specVersion 演进策略) */
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
@@ -112,6 +115,7 @@ public class ThemeRegistry {
|
||||
ThemeInfo info = MAPPER.readValue(resource.getInputStream(), ThemeInfo.class);
|
||||
ThemePackageValidator.validate(info, dirName, rel ->
|
||||
new ClassPathResource("templates/themes/" + dirName + "/" + rel).exists());
|
||||
applyMetaOverride(info);
|
||||
loaded.put(info.getName(), info);
|
||||
loadedSources.put(info.getName(), SOURCE_BUILTIN);
|
||||
} catch (Exception e) {
|
||||
@@ -131,6 +135,7 @@ public class ThemeRegistry {
|
||||
log.warn("外部主题包 {} 与内置包同名,忽略外部包(内置只读、优先,D1)", dirName);
|
||||
continue;
|
||||
}
|
||||
applyMetaOverride(info);
|
||||
loaded.put(info.getName(), info);
|
||||
loadedSources.put(info.getName(), SOURCE_EXTERNAL);
|
||||
} catch (Exception e) {
|
||||
@@ -229,9 +234,33 @@ public class ThemeRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 元数据覆盖层(任务四):包加载后读 {themeDir}/.meta/{name}.json 覆写 title/description——
|
||||
* 内置包 theme.json 在 jar 内只读,显示名/描述改名走此通道;损坏的覆盖层忽略(按原始元数据注册)。
|
||||
*/
|
||||
private void applyMetaOverride(ThemeInfo info) {
|
||||
if (themeDir == null || themeDir.isEmpty() || info.getName() == null) {
|
||||
return;
|
||||
}
|
||||
Path overrideFile = Paths.get(themeDir, META_OVERRIDE_DIR, info.getName() + ".json");
|
||||
if (!Files.isRegularFile(overrideFile)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ThemeInfo meta = MAPPER.readValue(overrideFile.toFile(), ThemeInfo.class);
|
||||
if (meta.getTitle() != null && !meta.getTitle().trim().isEmpty()) {
|
||||
info.setTitle(meta.getTitle().trim());
|
||||
}
|
||||
if (meta.getDescription() != null) {
|
||||
info.setDescription(meta.getDescription());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("元数据覆盖层 {} 读取失败,按原始元数据注册:{}", overrideFile, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/** W1:外部目录包扫描(themeDir 下各包的 theme.json;目录不存在视为空) */
|
||||
protected List<Path> scanExternalPackages() {
|
||||
List<Path> packages = new ArrayList<>();
|
||||
protected List<Path> scanExternalPackages() { List<Path> packages = new ArrayList<>();
|
||||
if (themeDir == null || themeDir.isEmpty()) {
|
||||
return packages;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user