feat(W1): 皮肤包完整管理-外部主题目录+热注册,zip上传/派生/编辑/删除,ThemePackageValidator收敛,Thymeleaf文件解析器,list增source/inUseCount/editable/usedBySiteNames,T-W1-01..20 全绿299/299
build / build-push (push) Successful in 52s
build / build-push (push) Successful in 52s
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
package com.yunhai.cms.controller.api;
|
||||
|
||||
import com.yunhai.cms.service.impl.ThemeService;
|
||||
import com.yunhai.cms.theme.ThemeInfo;
|
||||
import com.yunhai.cms.theme.ThemePackageService;
|
||||
import com.yunhai.cms.theme.ThemeRegistry;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -12,7 +13,14 @@ import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.yunhai.platform.utils.model.BoaUser;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.time.Duration;
|
||||
@@ -33,12 +41,53 @@ public class ThemeController {
|
||||
|
||||
private final ThemeService themeService;
|
||||
private final ThemeRegistry themeRegistry;
|
||||
private final ThemePackageService themePackageService;
|
||||
|
||||
@GetMapping("/list")
|
||||
public List<com.yunhai.cms.model.vo.ThemeVo> list() {
|
||||
return themeService.list();
|
||||
}
|
||||
|
||||
/** W1(G2):上传合规 zip——立即注册(免重启);不合规返回逐条校验报告,不落盘不注册 */
|
||||
@PostMapping("/upload")
|
||||
public com.yunhai.cms.model.vo.ThemeVo upload(BoaUser authUserWithDept, @RequestParam("file") MultipartFile file) {
|
||||
ThemeInfo info = themePackageService.upload(authUserWithDept, file);
|
||||
return themeService.voOf(info);
|
||||
}
|
||||
|
||||
/** W1(G3):从现有包派生新皮肤(复制包体+改名,外部包可编辑可删除) */
|
||||
@PostMapping("/derive")
|
||||
public com.yunhai.cms.model.vo.ThemeVo derive(BoaUser authUserWithDept,
|
||||
@RequestBody com.yunhai.cms.model.dto.ThemeDeriveRequest req) {
|
||||
ThemeInfo info = themePackageService.derive(authUserWithDept, req.getSource(), req.getName());
|
||||
return themeService.voOf(info);
|
||||
}
|
||||
|
||||
/** W1(G4):编辑外部包 theme.json 的 title 与参数 schema(保存后引用站点立即生效) */
|
||||
@PutMapping("/{name}/meta")
|
||||
public com.yunhai.cms.model.vo.ThemeVo updateMeta(BoaUser authUserWithDept,
|
||||
@PathVariable("name") String name,
|
||||
@RequestBody com.yunhai.cms.model.dto.ThemeMetaRequest req) {
|
||||
ThemeInfo info = themePackageService.updateMeta(authUserWithDept, name, req.getTitle(), req.getParams());
|
||||
return themeService.voOf(info);
|
||||
}
|
||||
|
||||
/** W1(G5):删除外部包(被用站点拒绝;内置不可删) */
|
||||
@DeleteMapping("/{name}")
|
||||
public java.util.Map<String, Object> remove(BoaUser authUserWithDept, @PathVariable("name") String name) {
|
||||
themePackageService.remove(authUserWithDept, name);
|
||||
java.util.Map<String, Object> result = new java.util.HashMap<>();
|
||||
result.put("ok", true);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** W1(G2/B4):热重载(运维直放目录后生效;default 缺失报错且旧集合不丢) */
|
||||
@PostMapping("/reload")
|
||||
public List<com.yunhai.cms.model.vo.ThemeVo> reload(BoaUser authUserWithDept) {
|
||||
themePackageService.reload(authUserWithDept);
|
||||
return themeService.list();
|
||||
}
|
||||
|
||||
@GetMapping("/{name}/asset/**")
|
||||
public ResponseEntity<Resource> asset(@PathVariable("name") String name,
|
||||
HttpServletRequest request) {
|
||||
@@ -49,7 +98,7 @@ public class ThemeController {
|
||||
if (path == null || path.isEmpty() || path.contains("..")) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
Resource resource = new ClassPathResource("templates/themes/" + name + "/assets/" + path);
|
||||
Resource resource = themeRegistry.resource(name, "assets/" + path);
|
||||
if (!resource.exists()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -65,8 +64,8 @@ public class PortalAssetController {
|
||||
if (path == null || path.isEmpty() || path.contains("..")) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
Resource resource = new ClassPathResource(
|
||||
"templates/themes/" + theme + "/assets/" + path);
|
||||
// W1(T-W1-20):内置→ClassPathResource、外部→FileSystemResource,统一走 Registry 定位
|
||||
Resource resource = themeRegistry.resource(theme, "assets/" + path);
|
||||
if (!resource.exists()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user