mirror of
https://gitee.com/liweiyi/ChestnutCMS.git
synced 2025-12-06 08:28:23 +08:00
版本更新:v1.4.5
This commit is contained in:
parent
58c8cea901
commit
c596a44483
@ -31,5 +31,5 @@ for NONE_IMAGE_ID in ${NONE_IMAGE_ID_ARR[*]}; do
|
|||||||
echo ">>>>>delete docker <none> image done: $NONE_IMAGE_ID"
|
echo ">>>>>delete docker <none> image done: $NONE_IMAGE_ID"
|
||||||
done
|
done
|
||||||
|
|
||||||
# 启动容器
|
# 启动容器,老版本命令是docker-compose up -d
|
||||||
docker-compose up -d
|
docker compose up -d
|
||||||
@ -17,13 +17,18 @@ chestnut:
|
|||||||
captchaType: math
|
captchaType: math
|
||||||
member:
|
member:
|
||||||
uploadPath: 'E:/dev/workspace_chestnut/_xy_member/'
|
uploadPath: 'E:/dev/workspace_chestnut/_xy_member/'
|
||||||
|
cms:
|
||||||
|
publish:
|
||||||
|
pool:
|
||||||
|
threadNamePrefix: "CMS-PUBLISH-"
|
||||||
|
queueCapacity: 10000
|
||||||
|
|
||||||
# 开发环境配置
|
# 开发环境配置
|
||||||
server:
|
server:
|
||||||
# 服务器的HTTP端口,默认为8080
|
# 服务器的HTTP端口,默认为8080
|
||||||
port: 8080
|
port: 8080
|
||||||
# 开启优雅停机
|
# 开启优雅停机
|
||||||
shutdown: graceful
|
shutdown: graceful
|
||||||
servlet:
|
servlet:
|
||||||
# 应用的访问路径
|
# 应用的访问路径
|
||||||
context-path: /
|
context-path: /
|
||||||
@ -50,12 +55,12 @@ spring:
|
|||||||
messages:
|
messages:
|
||||||
# 国际化资源文件路径
|
# 国际化资源文件路径
|
||||||
basename: i18n/messages
|
basename: i18n/messages
|
||||||
lifecycle:
|
lifecycle:
|
||||||
# 设置停机缓冲时间,默认:30s
|
# 设置停机缓冲时间,默认:30s
|
||||||
timeout-per-shutdown-phase: 20s
|
timeout-per-shutdown-phase: 20s
|
||||||
# 文件上传
|
# 文件上传
|
||||||
servlet:
|
servlet:
|
||||||
multipart:
|
multipart:
|
||||||
# 单个文件大小
|
# 单个文件大小
|
||||||
max-file-size: 100MB
|
max-file-size: 100MB
|
||||||
# 设置总上传的文件大小
|
# 设置总上传的文件大小
|
||||||
@ -84,8 +89,8 @@ spring:
|
|||||||
password: b18a03
|
password: b18a03
|
||||||
# 连接超时时间
|
# 连接超时时间
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
lettuce:
|
lettuce:
|
||||||
pool:
|
pool:
|
||||||
# 连接池中的最小空闲连接
|
# 连接池中的最小空闲连接
|
||||||
min-idle: 0
|
min-idle: 0
|
||||||
# 连接池中的最大空闲连接
|
# 连接池中的最大空闲连接
|
||||||
@ -120,7 +125,7 @@ spring:
|
|||||||
master:
|
master:
|
||||||
type: ${spring.datasource.type}
|
type: ${spring.datasource.type}
|
||||||
driverClassName: com.mysql.cj.jdbc.Driver
|
driverClassName: com.mysql.cj.jdbc.Driver
|
||||||
url: jdbc:mysql://127.0.0.1:3308/chestnut_cms?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
url: jdbc:mysql://127.0.0.1:3308/chestnut_cms?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||||
username: root
|
username: root
|
||||||
password: hello1234
|
password: hello1234
|
||||||
# 从库
|
# 从库
|
||||||
@ -199,24 +204,24 @@ management:
|
|||||||
logfile:
|
logfile:
|
||||||
external-file: ./logs/client.log
|
external-file: ./logs/client.log
|
||||||
|
|
||||||
sa-token:
|
sa-token:
|
||||||
# token名称 (同时也是cookie名称)
|
# token名称 (同时也是cookie名称)
|
||||||
token-name: Authorization
|
token-name: Authorization
|
||||||
# token前缀
|
# token前缀
|
||||||
token-prefix: Bearer
|
token-prefix: Bearer
|
||||||
# token有效期,单位s 默认30天, -1代表永不过期
|
# token有效期,单位s 默认30天, -1代表永不过期
|
||||||
timeout: 2592000
|
timeout: 2592000
|
||||||
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
|
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
|
||||||
active-timeout: -1
|
active-timeout: -1
|
||||||
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
||||||
is-concurrent: true
|
is-concurrent: true
|
||||||
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
||||||
is-share: true
|
is-share: true
|
||||||
# token风格
|
# token风格
|
||||||
token-style: uuid
|
token-style: uuid
|
||||||
# 是否输出操作日志
|
# 是否输出操作日志
|
||||||
is-log: true
|
is-log: true
|
||||||
|
|
||||||
# MyBatis配置
|
# MyBatis配置
|
||||||
mybatis-plus:
|
mybatis-plus:
|
||||||
global-config:
|
global-config:
|
||||||
@ -233,15 +238,15 @@ mybatis-plus:
|
|||||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
|
|
||||||
# 防止XSS攻击
|
# 防止XSS攻击
|
||||||
xss:
|
xss:
|
||||||
# 过滤开关
|
# 过滤开关
|
||||||
enabled: true
|
enabled: true
|
||||||
mode: clean
|
mode: clean
|
||||||
# 过滤链接
|
# 过滤链接
|
||||||
urlPatterns:
|
urlPatterns:
|
||||||
- /system/*
|
- /system/*
|
||||||
- /monitor/*
|
- /monitor/*
|
||||||
- /tool/*
|
- /tool/*
|
||||||
|
|
||||||
xxl:
|
xxl:
|
||||||
job:
|
job:
|
||||||
@ -254,7 +259,7 @@ xxl:
|
|||||||
### 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。
|
### 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。
|
||||||
#address:
|
#address:
|
||||||
### 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
|
### 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
|
||||||
ip:
|
ip:
|
||||||
### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
|
### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
|
||||||
port: 9968
|
port: 9968
|
||||||
### 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
|
### 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
|
||||||
|
|||||||
@ -20,7 +20,11 @@ chestnut:
|
|||||||
cms:
|
cms:
|
||||||
resourceRoot: /home/app/wwwroot_release
|
resourceRoot: /home/app/wwwroot_release
|
||||||
publish:
|
publish:
|
||||||
consumerCount: 1
|
pool:
|
||||||
|
threadNamePrefix: "CMS-PUBLISH-"
|
||||||
|
queueCapacity: 10000
|
||||||
|
coreSize: 2
|
||||||
|
maxSize: 4
|
||||||
|
|
||||||
# 开发环境配置
|
# 开发环境配置
|
||||||
server:
|
server:
|
||||||
|
|||||||
@ -15,27 +15,30 @@
|
|||||||
*/
|
*/
|
||||||
package com.chestnut.contentcore.config;
|
package com.chestnut.contentcore.config;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
||||||
|
|
||||||
import com.chestnut.common.redis.RedisCache;
|
import com.chestnut.common.redis.RedisCache;
|
||||||
import com.chestnut.common.utils.SpringUtils;
|
import com.chestnut.common.utils.SpringUtils;
|
||||||
import com.chestnut.common.utils.StringUtils;
|
import com.chestnut.common.utils.StringUtils;
|
||||||
import com.chestnut.common.utils.file.FileExUtils;
|
import com.chestnut.common.utils.file.FileExUtils;
|
||||||
import com.chestnut.contentcore.ContentCoreConsts;
|
import com.chestnut.contentcore.ContentCoreConsts;
|
||||||
import com.chestnut.contentcore.config.properties.CMSProperties;
|
import com.chestnut.contentcore.config.properties.CMSProperties;
|
||||||
|
import com.chestnut.contentcore.config.properties.CMSPublishProperties;
|
||||||
|
import com.chestnut.contentcore.publish.CmsStaticizeService;
|
||||||
|
import com.chestnut.contentcore.publish.IPublishStrategy;
|
||||||
|
import com.chestnut.contentcore.publish.strategies.ThreadPoolPublishStrategy;
|
||||||
import com.chestnut.system.fixed.config.BackendContext;
|
import com.chestnut.system.fixed.config.BackendContext;
|
||||||
|
|
||||||
import freemarker.cache.FileTemplateLoader;
|
import freemarker.cache.FileTemplateLoader;
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CMS配置
|
* CMS配置
|
||||||
@ -45,7 +48,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableConfigurationProperties(CMSProperties.class)
|
@EnableConfigurationProperties({ CMSProperties.class, CMSPublishProperties.class })
|
||||||
public class CMSConfig implements WebMvcConfigurer {
|
public class CMSConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
public static String CachePrefix = "cms:";
|
public static String CachePrefix = "cms:";
|
||||||
@ -117,4 +120,10 @@ public class CMSConfig implements WebMvcConfigurer {
|
|||||||
log.info("Clear redis caches with prefix `{}`", this.properties.getCacheName());
|
log.info("Clear redis caches with prefix `{}`", this.properties.getCacheName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(IPublishStrategy.class)
|
||||||
|
public IPublishStrategy publishStrategy(CMSPublishProperties publishProperties, CmsStaticizeService cmsStaticizeService) {
|
||||||
|
return new ThreadPoolPublishStrategy(publishProperties, cmsStaticizeService);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -28,9 +28,11 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
|||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@ConfigurationProperties(prefix = "chestnut.cms.publish")
|
@ConfigurationProperties(prefix = CMSPublishProperties.PREFIX)
|
||||||
public class CMSPublishProperties {
|
public class CMSPublishProperties {
|
||||||
|
|
||||||
|
public static final String PREFIX = "chestnut.cms.publish";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 启动时清理发布消息队列
|
* 启动时清理发布消息队列
|
||||||
*/
|
*/
|
||||||
@ -41,6 +43,11 @@ public class CMSPublishProperties {
|
|||||||
*/
|
*/
|
||||||
private int consumerCount = 2;
|
private int consumerCount = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布策略
|
||||||
|
*/
|
||||||
|
private String strategy;
|
||||||
|
|
||||||
private final AsyncProperties.Pool pool = new AsyncProperties.Pool();
|
private final AsyncProperties.Pool pool = new AsyncProperties.Pool();
|
||||||
|
|
||||||
private final AsyncProperties.Shutdown shutdown = new AsyncProperties.Shutdown();
|
private final AsyncProperties.Shutdown shutdown = new AsyncProperties.Shutdown();
|
||||||
|
|||||||
@ -188,14 +188,18 @@ public class CatalogController extends BaseRestController {
|
|||||||
*/
|
*/
|
||||||
@Priv(type = AdminUserType.TYPE, value = CmsPrivUtils.PRIV_SITE_VIEW_PLACEHOLDER)
|
@Priv(type = AdminUserType.TYPE, value = CmsPrivUtils.PRIV_SITE_VIEW_PLACEHOLDER)
|
||||||
@GetMapping("/treeData")
|
@GetMapping("/treeData")
|
||||||
public R<?> treeData() {
|
public R<?> treeData(@RequestParam(required = false, defaultValue = "false") Boolean disableLink) {
|
||||||
CmsSite site = this.siteService.getCurrentSite(ServletUtils.getRequest());
|
CmsSite site = this.siteService.getCurrentSite(ServletUtils.getRequest());
|
||||||
LoginUser loginUser = StpAdminUtil.getLoginUser();
|
LoginUser loginUser = StpAdminUtil.getLoginUser();
|
||||||
List<CmsCatalog> catalogs = this.catalogService.lambdaQuery().eq(CmsCatalog::getSiteId, site.getSiteId())
|
List<CmsCatalog> catalogs = this.catalogService.lambdaQuery().eq(CmsCatalog::getSiteId, site.getSiteId())
|
||||||
.orderByAsc(CmsCatalog::getSortFlag).list().stream().filter(c -> loginUser
|
.orderByAsc(CmsCatalog::getSortFlag).list().stream().filter(c -> loginUser
|
||||||
.hasPermission(CatalogPrivItem.View.getPermissionKey(c.getCatalogId())))
|
.hasPermission(CatalogPrivItem.View.getPermissionKey(c.getCatalogId())))
|
||||||
.toList();
|
.toList();
|
||||||
List<TreeNode<String>> treeData = catalogService.buildCatalogTreeData(catalogs);
|
List<TreeNode<String>> treeData = catalogService.buildCatalogTreeData(catalogs, (catalog, node) -> {
|
||||||
|
if (disableLink) {
|
||||||
|
node.setDisabled(CatalogType_Link.ID.equals(catalog.getCatalogType()));
|
||||||
|
}
|
||||||
|
});
|
||||||
return R.ok(Map.of("rows", treeData, "siteName", site.getName()));
|
return R.ok(Map.of("rows", treeData, "siteName", site.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -144,7 +144,9 @@ public class CoreController extends BaseRestController {
|
|||||||
// init templateType data to datamode
|
// init templateType data to datamode
|
||||||
ITemplateType templateType = this.templateService.getTemplateType(SiteTemplateType.TypeId);
|
ITemplateType templateType = this.templateService.getTemplateType(SiteTemplateType.TypeId);
|
||||||
templateType.initTemplateData(siteId, templateContext);
|
templateType.initTemplateData(siteId, templateContext);
|
||||||
templateContext.getVariables().put("Request", params);
|
templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, params);
|
||||||
|
// TODO 兼容历史版本,下个大版本移除IncludeRequest模板变量
|
||||||
|
templateContext.getVariables().put("IncludeRequest", params);
|
||||||
templateContext.getVariables().put("ClientType", ServletUtils.getDeviceType());
|
templateContext.getVariables().put("ClientType", ServletUtils.getDeviceType());
|
||||||
// staticize
|
// staticize
|
||||||
this.staticizeService.process(templateContext, ServletUtils.getResponse().getWriter());
|
this.staticizeService.process(templateContext, ServletUtils.getResponse().getWriter());
|
||||||
|
|||||||
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.chestnut.contentcore.controller;
|
||||||
|
|
||||||
|
import com.chestnut.common.domain.R;
|
||||||
|
import com.chestnut.common.security.anno.Priv;
|
||||||
|
import com.chestnut.common.security.web.BaseRestController;
|
||||||
|
import com.chestnut.contentcore.config.properties.CMSProperties;
|
||||||
|
import com.chestnut.contentcore.domain.vo.CmsConfigurationDashboardVO;
|
||||||
|
import com.chestnut.contentcore.publish.IPublishStrategy;
|
||||||
|
import com.chestnut.system.security.AdminUserType;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 首页看板数据
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
@Priv(type = AdminUserType.TYPE)
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/cms/dashboard/")
|
||||||
|
public class DashboardController extends BaseRestController {
|
||||||
|
|
||||||
|
private final IPublishStrategy publishStrategy;
|
||||||
|
|
||||||
|
private final CMSProperties properties;
|
||||||
|
|
||||||
|
@GetMapping("/config")
|
||||||
|
public R<?> getCmsConfiguration() {
|
||||||
|
CmsConfigurationDashboardVO vo = CmsConfigurationDashboardVO.builder()
|
||||||
|
.publishStrategy(publishStrategy.getId())
|
||||||
|
.resourceRoot(properties.getResourceRoot())
|
||||||
|
.build();
|
||||||
|
return R.ok(vo);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,11 +18,9 @@ package com.chestnut.contentcore.controller;
|
|||||||
import com.chestnut.common.domain.R;
|
import com.chestnut.common.domain.R;
|
||||||
import com.chestnut.common.security.anno.Priv;
|
import com.chestnut.common.security.anno.Priv;
|
||||||
import com.chestnut.common.security.web.BaseRestController;
|
import com.chestnut.common.security.web.BaseRestController;
|
||||||
import com.chestnut.contentcore.config.CMSPublishConfig;
|
import com.chestnut.contentcore.publish.IPublishStrategy;
|
||||||
import com.chestnut.system.security.AdminUserType;
|
import com.chestnut.system.security.AdminUserType;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.data.redis.connection.stream.StreamInfo;
|
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@ -40,15 +38,14 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@RequestMapping("/cms/publish/")
|
@RequestMapping("/cms/publish/")
|
||||||
public class PublishLogController extends BaseRestController {
|
public class PublishLogController extends BaseRestController {
|
||||||
|
|
||||||
private final StringRedisTemplate redisTemplate;
|
private final IPublishStrategy publishStrategy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发布队列任务数量
|
* 发布队列任务数量
|
||||||
*/
|
*/
|
||||||
@GetMapping("/taskCount")
|
@GetMapping("/taskCount")
|
||||||
public R<?> getPublishTaskCount() {
|
public R<?> getPublishTaskCount() {
|
||||||
StreamInfo.XInfoStream info = redisTemplate.opsForStream().info(CMSPublishConfig.PublishStreamName);
|
return R.ok(publishStrategy.getTaskCount());
|
||||||
return R.ok(info.streamLength());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,11 +53,7 @@ public class PublishLogController extends BaseRestController {
|
|||||||
*/
|
*/
|
||||||
@DeleteMapping("/clear")
|
@DeleteMapping("/clear")
|
||||||
public R<?> clearPublishTask() {
|
public R<?> clearPublishTask() {
|
||||||
redisTemplate.delete(CMSPublishConfig.PublishStreamName);
|
publishStrategy.cleanTasks();
|
||||||
try {
|
|
||||||
redisTemplate.opsForStream().createGroup(CMSPublishConfig.PublishStreamName, CMSPublishConfig.PublishConsumerGroup);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
return R.ok();
|
return R.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -178,8 +178,6 @@ public class TemplateController extends BaseRestController {
|
|||||||
fileName = FileExUtils.normalizePath(fileName);
|
fileName = FileExUtils.normalizePath(fileName);
|
||||||
String[] split = fileName.substring(0, fileName.indexOf(suffix)).split("/");
|
String[] split = fileName.substring(0, fileName.indexOf(suffix)).split("/");
|
||||||
for (String item : split) {
|
for (String item : split) {
|
||||||
System.out.println(item);
|
|
||||||
System.out.println(Pattern.matches("[a-zA-Z0-9_]+", item));
|
|
||||||
if (StringUtils.isEmpty(item) || !Pattern.matches("^[a-zA-Z0-9_]+$", item)) {
|
if (StringUtils.isEmpty(item) || !Pattern.matches("^[a-zA-Z0-9_]+$", item)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,24 +40,24 @@ public interface IPageWidget {
|
|||||||
/**
|
/**
|
||||||
* 页面部件基础数据实例
|
* 页面部件基础数据实例
|
||||||
*/
|
*/
|
||||||
public CmsPageWidget getPageWidgetEntity();
|
CmsPageWidget getPageWidgetEntity();
|
||||||
|
|
||||||
public void setPageWidgetEntity(CmsPageWidget cmsPageWdiget);
|
void setPageWidgetEntity(CmsPageWidget cmsPageWdiget);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 操作人
|
* 操作人
|
||||||
*
|
*
|
||||||
* @param loginUser
|
* @param loginUser
|
||||||
*/
|
*/
|
||||||
public void setOperator(LoginUser loginUser);
|
void setOperator(LoginUser loginUser);
|
||||||
|
|
||||||
public LoginUser getOperator();
|
LoginUser getOperator();
|
||||||
|
|
||||||
public void add();
|
void add();
|
||||||
|
|
||||||
public void save();
|
void save();
|
||||||
|
|
||||||
public void delete();
|
void delete();
|
||||||
|
|
||||||
public void publish() throws TemplateException, IOException;
|
void publish() throws TemplateException, IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.chestnut.contentcore.domain.vo;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Builder
|
||||||
|
public class CmsConfigurationDashboardVO {
|
||||||
|
|
||||||
|
private String publishStrategy;
|
||||||
|
|
||||||
|
private String resourceRoot;
|
||||||
|
}
|
||||||
@ -21,9 +21,10 @@ import com.chestnut.contentcore.domain.CmsCatalog;
|
|||||||
import com.chestnut.contentcore.domain.CmsContent;
|
import com.chestnut.contentcore.domain.CmsContent;
|
||||||
import com.chestnut.contentcore.domain.CmsSite;
|
import com.chestnut.contentcore.domain.CmsSite;
|
||||||
import com.chestnut.contentcore.fixed.dict.ContentStatus;
|
import com.chestnut.contentcore.fixed.dict.ContentStatus;
|
||||||
import com.chestnut.contentcore.publish.CatalogPublishTask;
|
import com.chestnut.contentcore.publish.staticize.CatalogStaticizeType;
|
||||||
import com.chestnut.contentcore.publish.ContentPublishTask;
|
import com.chestnut.contentcore.publish.staticize.ContentStaticizeType;
|
||||||
import com.chestnut.contentcore.publish.SitePublishTask;
|
import com.chestnut.contentcore.publish.staticize.SiteStaticizeType;
|
||||||
|
import com.chestnut.contentcore.publish.IPublishStrategy;
|
||||||
import com.chestnut.contentcore.service.ICatalogService;
|
import com.chestnut.contentcore.service.ICatalogService;
|
||||||
import com.chestnut.contentcore.service.IContentService;
|
import com.chestnut.contentcore.service.IContentService;
|
||||||
import com.chestnut.contentcore.service.ISiteService;
|
import com.chestnut.contentcore.service.ISiteService;
|
||||||
@ -56,11 +57,7 @@ public class SitePublishJobHandler extends IJobHandler implements IScheduledHand
|
|||||||
|
|
||||||
private final IContentService contentService;
|
private final IContentService contentService;
|
||||||
|
|
||||||
private final SitePublishTask sitePublisher;
|
private final IPublishStrategy publishStrategy;
|
||||||
|
|
||||||
private final CatalogPublishTask catalogPublisher;
|
|
||||||
|
|
||||||
private final ContentPublishTask contentPublisher;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
@ -92,16 +89,16 @@ public class SitePublishJobHandler extends IJobHandler implements IScheduledHand
|
|||||||
for (int i = 0; i * pageSize < total; i++) {
|
for (int i = 0; i * pageSize < total; i++) {
|
||||||
Page<CmsContent> page = contentService.page(new Page<>(i, pageSize, false), q);
|
Page<CmsContent> page = contentService.page(new Page<>(i, pageSize, false), q);
|
||||||
for (CmsContent xContent : page.getRecords()) {
|
for (CmsContent xContent : page.getRecords()) {
|
||||||
contentPublisher.publish(xContent);
|
publishStrategy.publish(ContentStaticizeType.TYPE, xContent.getContentId().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 发布栏目
|
// 发布栏目
|
||||||
for (CmsCatalog catalog : catalogList) {
|
for (CmsCatalog catalog : catalogList) {
|
||||||
catalogPublisher.publish(catalog);
|
publishStrategy.publish(CatalogStaticizeType.TYPE, catalog.getCatalogId().toString());
|
||||||
}
|
}
|
||||||
// 发布站点
|
// 发布站点
|
||||||
sitePublisher.publish(site);
|
publishStrategy.publish(SiteStaticizeType.TYPE, site.getSiteId().toString());
|
||||||
}
|
}
|
||||||
logger.info("Job '{}' completed, cost: {}ms", JOB_NAME, System.currentTimeMillis() - s);
|
logger.info("Job '{}' completed, cost: {}ms", JOB_NAME, System.currentTimeMillis() - s);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,7 +64,6 @@ public class ImageWatermarkArgsProperty implements IProperty {
|
|||||||
public ImageWatermarkArgs getPropValue(Map<String, String> configProps) {
|
public ImageWatermarkArgs getPropValue(Map<String, String> configProps) {
|
||||||
String v = MapUtils.getString(configProps, ID);
|
String v = MapUtils.getString(configProps, ID);
|
||||||
if (StringUtils.isNotEmpty(v)) {
|
if (StringUtils.isNotEmpty(v)) {
|
||||||
System.out.println(v);
|
|
||||||
return JacksonUtils.from(v, ImageWatermarkArgs.class);
|
return JacksonUtils.from(v, ImageWatermarkArgs.class);
|
||||||
}
|
}
|
||||||
return defaultValue();
|
return defaultValue();
|
||||||
|
|||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.chestnut.contentcore.publish;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CmsStaticizeService
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CmsStaticizeService {
|
||||||
|
|
||||||
|
private final Map<String, IStaticizeType> staticizeTypeMap;
|
||||||
|
|
||||||
|
public IStaticizeType getStaticizeType(String type) {
|
||||||
|
return staticizeTypeMap.get(IStaticizeType.BEAN_PREFIX + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package com.chestnut.contentcore.publish;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IPublishStrategy
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
public interface IPublishStrategy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布策略ID
|
||||||
|
*/
|
||||||
|
String getId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建发布任务
|
||||||
|
*
|
||||||
|
* @param dataType
|
||||||
|
* @param dataId
|
||||||
|
*/
|
||||||
|
void publish(String dataType, String dataId);
|
||||||
|
|
||||||
|
long getTaskCount();
|
||||||
|
|
||||||
|
void cleanTasks();
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package com.chestnut.contentcore.publish;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public interface IStaticizeType {
|
||||||
|
|
||||||
|
Logger logger = LoggerFactory.getLogger("publish");
|
||||||
|
|
||||||
|
String BEAN_PREFIX = "CmsStaticizeType";
|
||||||
|
|
||||||
|
String getType();
|
||||||
|
|
||||||
|
void staticize(String dataId);
|
||||||
|
}
|
||||||
@ -0,0 +1,167 @@
|
|||||||
|
package com.chestnut.contentcore.publish.staticize;
|
||||||
|
|
||||||
|
import com.chestnut.common.async.AsyncTaskManager;
|
||||||
|
import com.chestnut.common.staticize.StaticizeService;
|
||||||
|
import com.chestnut.common.staticize.core.TemplateContext;
|
||||||
|
import com.chestnut.common.utils.IdUtils;
|
||||||
|
import com.chestnut.common.utils.StringUtils;
|
||||||
|
import com.chestnut.common.utils.file.FileExUtils;
|
||||||
|
import com.chestnut.contentcore.core.impl.CatalogType_Link;
|
||||||
|
import com.chestnut.contentcore.core.impl.PublishPipeProp_DefaultListTemplate;
|
||||||
|
import com.chestnut.contentcore.core.impl.PublishPipeProp_IndexTemplate;
|
||||||
|
import com.chestnut.contentcore.core.impl.PublishPipeProp_ListTemplate;
|
||||||
|
import com.chestnut.contentcore.domain.CmsCatalog;
|
||||||
|
import com.chestnut.contentcore.domain.CmsPublishPipe;
|
||||||
|
import com.chestnut.contentcore.domain.CmsSite;
|
||||||
|
import com.chestnut.contentcore.properties.MaxPageOnContentPublishProperty;
|
||||||
|
import com.chestnut.contentcore.publish.IStaticizeType;
|
||||||
|
import com.chestnut.contentcore.service.ICatalogService;
|
||||||
|
import com.chestnut.contentcore.service.IPublishPipeService;
|
||||||
|
import com.chestnut.contentcore.service.ISiteService;
|
||||||
|
import com.chestnut.contentcore.service.ITemplateService;
|
||||||
|
import com.chestnut.contentcore.template.ITemplateType;
|
||||||
|
import com.chestnut.contentcore.template.impl.CatalogTemplateType;
|
||||||
|
import com.chestnut.contentcore.util.SiteUtils;
|
||||||
|
import com.chestnut.contentcore.util.TemplateUtils;
|
||||||
|
import freemarker.template.TemplateException;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CatalogStaticizeType
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Component(IStaticizeType.BEAN_PREFIX + CatalogStaticizeType.TYPE)
|
||||||
|
public class CatalogStaticizeType implements IStaticizeType {
|
||||||
|
|
||||||
|
public static final String TYPE = "catalog";
|
||||||
|
|
||||||
|
private final ISiteService siteService;
|
||||||
|
|
||||||
|
private final ICatalogService catalogService;
|
||||||
|
|
||||||
|
private final IPublishPipeService publishPipeService;
|
||||||
|
|
||||||
|
private final ITemplateService templateService;
|
||||||
|
|
||||||
|
private final StaticizeService staticizeService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void staticize(String dataId) {
|
||||||
|
Long catalogId = Long.valueOf(dataId);
|
||||||
|
if (IdUtils.validate(catalogId)) {
|
||||||
|
CmsCatalog catalog = this.catalogService.getCatalog(catalogId);
|
||||||
|
if (Objects.nonNull(catalog)) {
|
||||||
|
this.catalogStaticize(catalog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void catalogStaticize(CmsCatalog catalog) {
|
||||||
|
CmsSite site = this.siteService.getSite(catalog.getSiteId());
|
||||||
|
int maxPage = MaxPageOnContentPublishProperty.getValue(site.getConfigProps());
|
||||||
|
this.catalogStaticize(catalog, maxPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void catalogStaticize(CmsCatalog catalog, int pageMax) {
|
||||||
|
if (!catalog.isStaticize() || !catalog.isVisible() || CatalogType_Link.ID.equals(catalog.getCatalogType())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<CmsPublishPipe> publishPipes = this.publishPipeService.getPublishPipes(catalog.getSiteId());
|
||||||
|
for (CmsPublishPipe pp : publishPipes) {
|
||||||
|
this.doCatalogStaticize(catalog, pp.getCode(), pageMax);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doCatalogStaticize(CmsCatalog catalog, String publishPipeCode, int pageMax) {
|
||||||
|
CmsSite site = this.siteService.getSite(catalog.getSiteId());
|
||||||
|
if (!catalog.isStaticize()) {
|
||||||
|
logger.warn("【{}】未启用静态化的栏目跳过静态化:{}", publishPipeCode, catalog.getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!catalog.isVisible()) {
|
||||||
|
logger.warn("【{}】不可见状态的栏目跳过静态化:{}", publishPipeCode, catalog.getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (CatalogType_Link.ID.equals(catalog.getCatalogType())) {
|
||||||
|
logger.warn("【{}】链接类型栏目跳过静态化:{}", publishPipeCode, catalog.getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String indexTemplate = PublishPipeProp_IndexTemplate.getValue(publishPipeCode, catalog.getPublishPipeProps());
|
||||||
|
String listTemplate = PublishPipeProp_ListTemplate.getValue(publishPipeCode, catalog.getPublishPipeProps());
|
||||||
|
if (StringUtils.isEmpty(listTemplate)) {
|
||||||
|
listTemplate = PublishPipeProp_DefaultListTemplate.getValue(publishPipeCode, site.getPublishPipeProps()); // 取站点默认模板
|
||||||
|
}
|
||||||
|
File indexTemplateFile = this.templateService.findTemplateFile(site, indexTemplate, publishPipeCode);
|
||||||
|
File listTemplateFile = this.templateService.findTemplateFile(site, listTemplate, publishPipeCode);
|
||||||
|
if (indexTemplateFile == null && listTemplateFile == null) {
|
||||||
|
logger.warn(AsyncTaskManager.addErrMessage(StringUtils.messageFormat("[{0}]栏目首页模板和列表页模板未配置或不存在:{1}",
|
||||||
|
publishPipeCode, catalog.getCatalogId() + "#" + catalog.getName())));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String siteRoot = SiteUtils.getSiteRoot(site, publishPipeCode);
|
||||||
|
String dirPath = siteRoot + catalog.getPath();
|
||||||
|
FileExUtils.mkdirs(dirPath);
|
||||||
|
String staticSuffix = site.getStaticSuffix(publishPipeCode); // 静态化文件类型
|
||||||
|
|
||||||
|
// 发布栏目首页
|
||||||
|
long s = System.currentTimeMillis();
|
||||||
|
if (Objects.nonNull(indexTemplateFile)) {
|
||||||
|
try {
|
||||||
|
String templateKey = SiteUtils.getTemplateKey(site, publishPipeCode, indexTemplate);
|
||||||
|
TemplateContext templateContext = new TemplateContext(templateKey, false, publishPipeCode);
|
||||||
|
templateContext.setDirectory(dirPath);
|
||||||
|
templateContext.setFirstFileName("index" + StringUtils.DOT + staticSuffix);
|
||||||
|
// init template variables
|
||||||
|
TemplateUtils.initGlobalVariables(site, templateContext);
|
||||||
|
// init templateType variables
|
||||||
|
ITemplateType templateType = templateService.getTemplateType(CatalogTemplateType.TypeId);
|
||||||
|
templateType.initTemplateData(catalog.getCatalogId(), templateContext);
|
||||||
|
// staticize
|
||||||
|
this.staticizeService.process(templateContext);
|
||||||
|
logger.debug("[{}]栏目首页模板解析:{},耗时:{}ms", publishPipeCode, catalog.getCatalogId() + "#" + catalog.getName(), (System.currentTimeMillis() - s));
|
||||||
|
} catch (IOException | TemplateException e) {
|
||||||
|
logger.error(AsyncTaskManager.addErrMessage(StringUtils.messageFormat("[{0}]栏目首页解析失败:{1}",
|
||||||
|
publishPipeCode, catalog.getCatalogId() + "#" + catalog.getName())), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 发布栏目列表页
|
||||||
|
if (Objects.nonNull(listTemplateFile)) {
|
||||||
|
s = System.currentTimeMillis();
|
||||||
|
try {
|
||||||
|
String templateKey = SiteUtils.getTemplateKey(site, publishPipeCode, listTemplate);
|
||||||
|
TemplateContext templateContext = new TemplateContext(templateKey, false, publishPipeCode);
|
||||||
|
templateContext.setMaxPageNo(pageMax);
|
||||||
|
templateContext.setDirectory(dirPath);
|
||||||
|
String name = Objects.nonNull(indexTemplateFile) ? "list" : "index";
|
||||||
|
templateContext.setFirstFileName(name + StringUtils.DOT + staticSuffix);
|
||||||
|
templateContext.setOtherFileName(
|
||||||
|
name + "_" + TemplateContext.PlaceHolder_PageNo + StringUtils.DOT + staticSuffix);
|
||||||
|
// init template variables
|
||||||
|
TemplateUtils.initGlobalVariables(site, templateContext);
|
||||||
|
// init templateType variables
|
||||||
|
ITemplateType templateType = templateService.getTemplateType(CatalogTemplateType.TypeId);
|
||||||
|
templateType.initTemplateData(catalog.getCatalogId(), templateContext);
|
||||||
|
// staticize
|
||||||
|
this.staticizeService.process(templateContext);
|
||||||
|
logger.debug("[{}]栏目列表模板解析:{},耗时:{}ms", publishPipeCode, catalog.getCatalogId() + "#" + catalog.getName(), (System.currentTimeMillis() - s));
|
||||||
|
} catch (Exception e1) {
|
||||||
|
logger.error(AsyncTaskManager.addErrMessage(StringUtils.messageFormat("[{0}]栏目列表页解析失败:{1}",
|
||||||
|
publishPipeCode, catalog.getCatalogId() + "#" + catalog.getName())), e1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,208 @@
|
|||||||
|
package com.chestnut.contentcore.publish.staticize;
|
||||||
|
|
||||||
|
import com.chestnut.common.async.AsyncTaskManager;
|
||||||
|
import com.chestnut.common.staticize.StaticizeService;
|
||||||
|
import com.chestnut.common.staticize.core.TemplateContext;
|
||||||
|
import com.chestnut.common.utils.IdUtils;
|
||||||
|
import com.chestnut.common.utils.StringUtils;
|
||||||
|
import com.chestnut.contentcore.core.IPublishPipeProp;
|
||||||
|
import com.chestnut.contentcore.core.impl.PublishPipeProp_ContentTemplate;
|
||||||
|
import com.chestnut.contentcore.domain.CmsCatalog;
|
||||||
|
import com.chestnut.contentcore.domain.CmsContent;
|
||||||
|
import com.chestnut.contentcore.domain.CmsPublishPipe;
|
||||||
|
import com.chestnut.contentcore.domain.CmsSite;
|
||||||
|
import com.chestnut.contentcore.publish.IStaticizeType;
|
||||||
|
import com.chestnut.contentcore.service.*;
|
||||||
|
import com.chestnut.contentcore.template.ITemplateType;
|
||||||
|
import com.chestnut.contentcore.template.impl.ContentTemplateType;
|
||||||
|
import com.chestnut.contentcore.util.ContentUtils;
|
||||||
|
import com.chestnut.contentcore.util.SiteUtils;
|
||||||
|
import com.chestnut.contentcore.util.TemplateUtils;
|
||||||
|
import freemarker.template.TemplateException;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ContentStaticizeType
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Component(IStaticizeType.BEAN_PREFIX + ContentStaticizeType.TYPE)
|
||||||
|
public class ContentStaticizeType implements IStaticizeType {
|
||||||
|
|
||||||
|
public static final String TYPE = "content";
|
||||||
|
|
||||||
|
private final ISiteService siteService;
|
||||||
|
|
||||||
|
private final ICatalogService catalogService;
|
||||||
|
|
||||||
|
private final IContentService contentService;
|
||||||
|
|
||||||
|
private final IPublishPipeService publishPipeService;
|
||||||
|
|
||||||
|
private final ITemplateService templateService;
|
||||||
|
|
||||||
|
private final StaticizeService staticizeService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void staticize(String dataId) {
|
||||||
|
Long contentId = Long.valueOf(dataId);
|
||||||
|
if (IdUtils.validate(contentId)) {
|
||||||
|
CmsContent content = this.contentService.getById(contentId);
|
||||||
|
if (Objects.nonNull(content)) {
|
||||||
|
this.contentStaticize(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void contentStaticize(CmsContent cmsContent) {
|
||||||
|
List<CmsPublishPipe> publishPipes = publishPipeService.getPublishPipes(cmsContent.getSiteId());
|
||||||
|
// 发布内容
|
||||||
|
for (CmsPublishPipe pp : publishPipes) {
|
||||||
|
doContentStaticize(cmsContent, pp.getCode());
|
||||||
|
// 内容扩展模板静态化
|
||||||
|
doContentExStaticize(cmsContent, pp.getCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doContentStaticize(CmsContent content, String publishPipeCode) {
|
||||||
|
CmsSite site = this.siteService.getSite(content.getSiteId());
|
||||||
|
CmsCatalog catalog = this.catalogService.getCatalog(content.getCatalogId());
|
||||||
|
if (!catalog.isStaticize()) {
|
||||||
|
logger.warn("[ {} ]栏目设置不静态化[ {}#{} ]:{}", publishPipeCode, site.getName(),
|
||||||
|
catalog.getName(), content.getTitle());
|
||||||
|
return; // 不静态化直接跳过
|
||||||
|
}
|
||||||
|
if (content.isLinkContent()) {
|
||||||
|
logger.warn("[ {} ]标题内容不需要静态化[ {}#{} ]:{}", publishPipeCode, site.getName(),
|
||||||
|
catalog.getName(), content.getTitle());
|
||||||
|
return; // 标题内容不需要静态化
|
||||||
|
}
|
||||||
|
final String detailTemplate = getDetailTemplate(site, catalog, content, publishPipeCode);
|
||||||
|
File templateFile = this.templateService.findTemplateFile(site, detailTemplate, publishPipeCode);
|
||||||
|
if (templateFile == null) {
|
||||||
|
logger.warn(AsyncTaskManager.addErrMessage(
|
||||||
|
StringUtils.messageFormat("[ {0} ]内容模板未设置或文件不存在[ {1}#{2} ]:{3}",
|
||||||
|
publishPipeCode, site.getName(), catalog.getName(), content.getTitle())));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
long s = System.currentTimeMillis();
|
||||||
|
// 自定义模板上下文
|
||||||
|
String templateKey = SiteUtils.getTemplateKey(site, publishPipeCode, detailTemplate);
|
||||||
|
TemplateContext templateContext = new TemplateContext(templateKey, false, publishPipeCode);
|
||||||
|
// init template datamode
|
||||||
|
TemplateUtils.initGlobalVariables(site, templateContext);
|
||||||
|
// init templateType data to datamode
|
||||||
|
ITemplateType templateType = this.templateService.getTemplateType(ContentTemplateType.TypeId);
|
||||||
|
templateType.initTemplateData(content.getContentId(), templateContext);
|
||||||
|
// 静态化文件地址
|
||||||
|
this.setContentStaticPath(site, catalog, content, templateContext);
|
||||||
|
// 静态化
|
||||||
|
this.staticizeService.process(templateContext);
|
||||||
|
logger.debug("[ {} ]内容详情页模板解析[ {}#{} ]:{},耗时:{}ms", publishPipeCode, site.getName(),
|
||||||
|
catalog.getName(), content.getTitle(), (System.currentTimeMillis() - s));
|
||||||
|
} catch (TemplateException | IOException e) {
|
||||||
|
logger.error(AsyncTaskManager.addErrMessage(StringUtils.messageFormat("[{0}]内容详情页解析失败:[{1}]{2}",
|
||||||
|
publishPipeCode, catalog.getName(), content.getTitle())), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setContentStaticPath(CmsSite site, CmsCatalog catalog, CmsContent content, TemplateContext context) {
|
||||||
|
String siteRoot = SiteUtils.getSiteRoot(site, context.getPublishPipeCode());
|
||||||
|
if (StringUtils.isNotBlank(content.getStaticPath())) {
|
||||||
|
String dir = "";
|
||||||
|
String filename = content.getStaticPath();
|
||||||
|
if (filename.indexOf("/") > 0) {
|
||||||
|
dir = filename.substring(0, filename.lastIndexOf("/") + 1);
|
||||||
|
filename = filename.substring(filename.lastIndexOf("/") + 1);
|
||||||
|
}
|
||||||
|
context.setDirectory(siteRoot + dir);
|
||||||
|
context.setFirstFileName(filename);
|
||||||
|
String name = filename.substring(0, filename.lastIndexOf("."));
|
||||||
|
String suffix = filename.substring(filename.lastIndexOf("."));
|
||||||
|
context.setOtherFileName(name + "_" + TemplateContext.PlaceHolder_PageNo + suffix);
|
||||||
|
} else {
|
||||||
|
context.setDirectory(siteRoot + catalog.getPath());
|
||||||
|
String suffix = site.getStaticSuffix(context.getPublishPipeCode());
|
||||||
|
context.setFirstFileName(content.getContentId() + StringUtils.DOT + suffix);
|
||||||
|
context.setOtherFileName(
|
||||||
|
content.getContentId() + "_" + TemplateContext.PlaceHolder_PageNo + StringUtils.DOT + suffix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doContentExStaticize(CmsContent content, String publishPipeCode) {
|
||||||
|
CmsSite site = this.siteService.getSite(content.getSiteId());
|
||||||
|
CmsCatalog catalog = this.catalogService.getCatalog(content.getCatalogId());
|
||||||
|
if (!catalog.isStaticize()) {
|
||||||
|
logger.warn("[{}]栏目设置不静态化[{}#{}]:{}", publishPipeCode, site.getName(), catalog.getName(), content.getTitle());
|
||||||
|
return; // 不静态化直接跳过
|
||||||
|
}
|
||||||
|
if (content.isLinkContent()) {
|
||||||
|
logger.warn("[{}]标题内容不需要静态化[ {}#{} ]:{}", publishPipeCode, site.getName(), catalog.getName(), content.getTitle());
|
||||||
|
return; // 标题内容不需要静态化
|
||||||
|
}
|
||||||
|
String exTemplate = ContentUtils.getContentExTemplate(content, catalog, publishPipeCode);
|
||||||
|
if (StringUtils.isEmpty(exTemplate)) {
|
||||||
|
return; // 未设置扩展模板直接跳过
|
||||||
|
}
|
||||||
|
File templateFile = this.templateService.findTemplateFile(site, exTemplate, publishPipeCode);
|
||||||
|
if (templateFile == null) {
|
||||||
|
logger.warn("[{}]内容扩展模板未设置或文件不存在[ {}#{} ]:{}", publishPipeCode, site.getName(), catalog.getName(), content.getTitle());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
long s = System.currentTimeMillis();
|
||||||
|
// 自定义模板上下文
|
||||||
|
String templateKey = SiteUtils.getTemplateKey(site, publishPipeCode, exTemplate);
|
||||||
|
TemplateContext templateContext = new TemplateContext(templateKey, false, publishPipeCode);
|
||||||
|
// init template datamode
|
||||||
|
TemplateUtils.initGlobalVariables(site, templateContext);
|
||||||
|
// init templateType data to datamode
|
||||||
|
ITemplateType templateType = this.templateService.getTemplateType(ContentTemplateType.TypeId);
|
||||||
|
templateType.initTemplateData(content.getContentId(), templateContext);
|
||||||
|
// 静态化文件地址
|
||||||
|
String siteRoot = SiteUtils.getSiteRoot(site, publishPipeCode);
|
||||||
|
templateContext.setDirectory(siteRoot + catalog.getPath());
|
||||||
|
String fileName = ContentUtils.getContextExFileName(content.getContentId(), site.getStaticSuffix(publishPipeCode));
|
||||||
|
templateContext.setFirstFileName(fileName);
|
||||||
|
// 静态化
|
||||||
|
this.staticizeService.process(templateContext);
|
||||||
|
logger.debug("[{}]内容扩展模板解析[ {}#{} ]:{},耗时:{}ms", publishPipeCode, site.getName(),
|
||||||
|
catalog.getName(), content.getTitle(), (System.currentTimeMillis() - s));
|
||||||
|
} catch (TemplateException | IOException e) {
|
||||||
|
logger.error(AsyncTaskManager.addErrMessage(StringUtils.messageFormat("[{0}] 内容扩展模板解析失败 [{1}#{2}]:{3}",
|
||||||
|
publishPipeCode, site.getName(), catalog.getName(), content.getTitle())), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDetailTemplate(CmsSite site, CmsCatalog catalog, CmsContent content, String publishPipeCode) {
|
||||||
|
String detailTemplate = PublishPipeProp_ContentTemplate.getValue(publishPipeCode,
|
||||||
|
content.getPublishPipeProps());
|
||||||
|
if (StringUtils.isEmpty(detailTemplate)) {
|
||||||
|
// 无内容独立模板取栏目配置
|
||||||
|
detailTemplate = this.publishPipeService.getPublishPipePropValue(
|
||||||
|
IPublishPipeProp.DetailTemplatePropPrefix + content.getContentType(), publishPipeCode,
|
||||||
|
catalog.getPublishPipeProps());
|
||||||
|
if (StringUtils.isEmpty(detailTemplate)) {
|
||||||
|
// 无栏目配置去站点默认模板配置
|
||||||
|
detailTemplate = this.publishPipeService.getPublishPipePropValue(
|
||||||
|
IPublishPipeProp.DefaultDetailTemplatePropPrefix + content.getContentType(), publishPipeCode,
|
||||||
|
site.getPublishPipeProps());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return detailTemplate;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,95 @@
|
|||||||
|
package com.chestnut.contentcore.publish.staticize;
|
||||||
|
|
||||||
|
import com.chestnut.common.async.AsyncTaskManager;
|
||||||
|
import com.chestnut.common.staticize.StaticizeService;
|
||||||
|
import com.chestnut.common.staticize.core.TemplateContext;
|
||||||
|
import com.chestnut.common.utils.IdUtils;
|
||||||
|
import com.chestnut.common.utils.StringUtils;
|
||||||
|
import com.chestnut.contentcore.domain.CmsSite;
|
||||||
|
import com.chestnut.contentcore.publish.IStaticizeType;
|
||||||
|
import com.chestnut.contentcore.service.IPublishPipeService;
|
||||||
|
import com.chestnut.contentcore.service.ISiteService;
|
||||||
|
import com.chestnut.contentcore.service.ITemplateService;
|
||||||
|
import com.chestnut.contentcore.template.ITemplateType;
|
||||||
|
import com.chestnut.contentcore.template.impl.SiteTemplateType;
|
||||||
|
import com.chestnut.contentcore.util.SiteUtils;
|
||||||
|
import com.chestnut.contentcore.util.TemplateUtils;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SiteStaticizeType
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Component(IStaticizeType.BEAN_PREFIX + SiteStaticizeType.TYPE)
|
||||||
|
public class SiteStaticizeType implements IStaticizeType {
|
||||||
|
|
||||||
|
public static final String TYPE = "site";
|
||||||
|
|
||||||
|
private final ISiteService siteService;
|
||||||
|
|
||||||
|
private final IPublishPipeService publishPipeService;
|
||||||
|
|
||||||
|
private final ITemplateService templateService;
|
||||||
|
|
||||||
|
private final StaticizeService staticizeService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void staticize(String dataId) {
|
||||||
|
Long siteId = Long.valueOf(dataId);
|
||||||
|
if (IdUtils.validate(siteId)) {
|
||||||
|
CmsSite site = this.siteService.getSite(siteId);
|
||||||
|
if (Objects.nonNull(site)) {
|
||||||
|
this.siteStaticize(site);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void siteStaticize(CmsSite site) {
|
||||||
|
this.publishPipeService.getPublishPipes(site.getSiteId())
|
||||||
|
.forEach(pp -> doSiteStaticize(site, pp.getCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doSiteStaticize(CmsSite site, String publishPipeCode) {
|
||||||
|
try {
|
||||||
|
AsyncTaskManager
|
||||||
|
.setTaskMessage(StringUtils.messageFormat("[{0}]正在发布站点首页:{1}", publishPipeCode, site.getName()));
|
||||||
|
|
||||||
|
String indexTemplate = site.getIndexTemplate(publishPipeCode);
|
||||||
|
File templateFile = this.templateService.findTemplateFile(site, indexTemplate, publishPipeCode);
|
||||||
|
if (Objects.isNull(templateFile)) {
|
||||||
|
logger.warn(AsyncTaskManager.addErrMessage(StringUtils.messageFormat("[{0}]站点首页模板未配置或不存在:{1}",
|
||||||
|
publishPipeCode, site.getSiteId() + "#" + site.getName())));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 模板ID = 通道:站点目录:模板文件名
|
||||||
|
String templateKey = SiteUtils.getTemplateKey(site, publishPipeCode, indexTemplate);
|
||||||
|
TemplateContext context = new TemplateContext(templateKey, false, publishPipeCode);
|
||||||
|
// init template datamode
|
||||||
|
TemplateUtils.initGlobalVariables(site, context);
|
||||||
|
// init templateType data to datamode
|
||||||
|
ITemplateType templateType = templateService.getTemplateType(SiteTemplateType.TypeId);
|
||||||
|
templateType.initTemplateData(site.getSiteId(), context);
|
||||||
|
|
||||||
|
long s = System.currentTimeMillis();
|
||||||
|
context.setDirectory(SiteUtils.getSiteRoot(site, publishPipeCode));
|
||||||
|
context.setFirstFileName("index" + StringUtils.DOT + site.getStaticSuffix(publishPipeCode));
|
||||||
|
this.staticizeService.process(context);
|
||||||
|
logger.debug("[{}]首页模板解析:{},耗时:{}ms", publishPipeCode, site.getName(), (System.currentTimeMillis() - s));
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(AsyncTaskManager.addErrMessage(StringUtils.messageFormat("[{0}][{1}]站点首页解析失败:{2}",
|
||||||
|
publishPipeCode, site.getName(), e.getMessage())), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.chestnut.contentcore.publish.strategies;
|
||||||
|
|
||||||
|
import com.chestnut.contentcore.publish.CmsStaticizeService;
|
||||||
|
import com.chestnut.contentcore.publish.IStaticizeType;
|
||||||
|
import com.chestnut.contentcore.publish.strategies.RedisStreamPublishStrategy;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.collections4.MapUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.data.redis.connection.stream.Consumer;
|
||||||
|
import org.springframework.data.redis.connection.stream.MapRecord;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.data.redis.stream.StreamListener;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布任务消费监听
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class PublishTaskReceiver implements StreamListener<String, MapRecord<String, String, String>> {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger("publish");
|
||||||
|
|
||||||
|
private final CmsStaticizeService cmsStaticizeService;
|
||||||
|
|
||||||
|
private final StringRedisTemplate redisTemplate;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
private Consumer consumer;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessage(MapRecord<String, String, String> message) {
|
||||||
|
String stream = message.getStream();
|
||||||
|
if (Objects.nonNull(stream)) {
|
||||||
|
try {
|
||||||
|
Map<String, String> map = message.getValue();
|
||||||
|
String type = MapUtils.getString(map, "type");
|
||||||
|
|
||||||
|
IStaticizeType staticizeType = cmsStaticizeService.getStaticizeType(type);
|
||||||
|
if (Objects.nonNull(staticizeType)) {
|
||||||
|
staticizeType.staticize(map.get("id"));
|
||||||
|
}
|
||||||
|
} catch(Exception e) {
|
||||||
|
logger.error("Publish err.", e);
|
||||||
|
} finally {
|
||||||
|
redisTemplate.opsForStream().acknowledge(
|
||||||
|
stream,
|
||||||
|
RedisStreamPublishStrategy.PublishConsumerGroup,
|
||||||
|
message.getId().getValue()
|
||||||
|
);
|
||||||
|
redisTemplate.opsForStream().delete(stream, message.getId().getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,99 @@
|
|||||||
|
package com.chestnut.contentcore.publish.strategies;
|
||||||
|
|
||||||
|
import com.chestnut.common.async.AsyncTask;
|
||||||
|
import com.chestnut.contentcore.config.properties.CMSPublishProperties;
|
||||||
|
import com.chestnut.contentcore.publish.CmsStaticizeService;
|
||||||
|
import com.chestnut.contentcore.publish.IPublishStrategy;
|
||||||
|
import com.chestnut.contentcore.publish.IStaticizeType;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||||
|
import org.springframework.scheduling.support.PeriodicTrigger;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布策略:Redis Set
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@ConditionalOnProperty(prefix = CMSPublishProperties.PREFIX, name = "strategy", havingValue = RedisSetPublishStrategy.ID)
|
||||||
|
public class RedisSetPublishStrategy implements IPublishStrategy, CommandLineRunner {
|
||||||
|
|
||||||
|
public static final String ID = "RedisSet";
|
||||||
|
|
||||||
|
static final String CACHE_NAME = "cms:publish:list";
|
||||||
|
|
||||||
|
private final StringRedisTemplate redisTemplate;
|
||||||
|
|
||||||
|
private final CMSPublishProperties properties;
|
||||||
|
|
||||||
|
private final CmsStaticizeService cmsStaticizeService;
|
||||||
|
|
||||||
|
private final ThreadPoolTaskScheduler threadPoolTaskScheduler;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publish(String dataType, String dataId) {
|
||||||
|
redisTemplate.opsForSet().add(CACHE_NAME, dataType+"-"+dataId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTaskCount() {
|
||||||
|
Long size = redisTemplate.opsForSet().size(CACHE_NAME);
|
||||||
|
return Objects.requireNonNullElse(size, 0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanTasks() {
|
||||||
|
redisTemplate.opsForSet().remove(CACHE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(String... args) throws Exception {
|
||||||
|
for (int i = 1; i <= properties.getConsumerCount(); i++) {
|
||||||
|
// 创建一个3秒间隔执行的定时任务
|
||||||
|
PeriodicTrigger periodicTrigger = new PeriodicTrigger(Duration.ofSeconds(3L));
|
||||||
|
periodicTrigger.setFixedRate(false);
|
||||||
|
|
||||||
|
AsyncTask task = new AsyncTask() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run0() {
|
||||||
|
String data = null;
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
data = redisTemplate.opsForSet().pop(CACHE_NAME);
|
||||||
|
if (Objects.nonNull(data)) {
|
||||||
|
String[] split = data.split("-");
|
||||||
|
String dataType = split[0];
|
||||||
|
String dataId = split[1];
|
||||||
|
|
||||||
|
IStaticizeType staticizeType = cmsStaticizeService.getStaticizeType(dataType);
|
||||||
|
if (Objects.nonNull(staticizeType)) {
|
||||||
|
staticizeType.staticize(dataId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
IStaticizeType.logger.error("静态化失败", e);
|
||||||
|
}
|
||||||
|
} while(Objects.nonNull(data));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
task.setTaskId("cms-publish-" + i);
|
||||||
|
task.setType("CMS-PUBLISH");
|
||||||
|
threadPoolTaskScheduler.schedule(task, periodicTrigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,120 @@
|
|||||||
|
package com.chestnut.contentcore.publish.strategies;
|
||||||
|
|
||||||
|
import com.chestnut.contentcore.config.properties.CMSPublishProperties;
|
||||||
|
import com.chestnut.contentcore.publish.CmsStaticizeService;
|
||||||
|
import com.chestnut.contentcore.publish.IPublishStrategy;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.data.redis.connection.stream.*;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.data.redis.stream.StreamMessageListenerContainer;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布策略:Redis Stream
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@ConditionalOnProperty(prefix = CMSPublishProperties.PREFIX, name = "strategy", havingValue = RedisStreamPublishStrategy.ID)
|
||||||
|
public class RedisStreamPublishStrategy implements IPublishStrategy {
|
||||||
|
|
||||||
|
public static final String ID = "RedisStream";
|
||||||
|
|
||||||
|
public static final String PublishStreamName = "ChestnutCMSPublishStream";
|
||||||
|
|
||||||
|
public static final String PublishConsumerGroup = "ChestnutCMSPublishConsumerGroup";
|
||||||
|
|
||||||
|
private final CMSPublishProperties properties;
|
||||||
|
|
||||||
|
private final StringRedisTemplate redisTemplate;
|
||||||
|
|
||||||
|
private final CmsStaticizeService cmsStaticizeService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publish(String dataType, String dataId) {
|
||||||
|
MapRecord<String, String, String> record = MapRecord.create(PublishStreamName, Map.of(
|
||||||
|
"type", dataType,
|
||||||
|
"id", dataId
|
||||||
|
));
|
||||||
|
redisTemplate.opsForStream().add(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTaskCount() {
|
||||||
|
StreamInfo.XInfoStream info = redisTemplate.opsForStream().info(PublishStreamName);
|
||||||
|
return info.streamLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanTasks() {
|
||||||
|
try {
|
||||||
|
redisTemplate.delete(PublishStreamName);
|
||||||
|
redisTemplate.opsForStream().createGroup(PublishStreamName, PublishConsumerGroup);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public StreamMessageListenerContainer<String, MapRecord<String, String, String>> streamMessageListenerContainer() {
|
||||||
|
// 启动清理消息队列数据
|
||||||
|
if (properties.isClearOnStart()) {
|
||||||
|
redisTemplate.delete(PublishStreamName);
|
||||||
|
}
|
||||||
|
// 监听容器配置
|
||||||
|
StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, MapRecord<String, String, String>> streamMessageListenerContainerOptions = StreamMessageListenerContainer.StreamMessageListenerContainerOptions
|
||||||
|
.builder()
|
||||||
|
.batchSize(10) // 一次拉取消息数量
|
||||||
|
.pollTimeout(Duration.ofSeconds(2)) // 拉取消息超时时间
|
||||||
|
.executor(cmsPublishThreadPoolTaskExecutor())
|
||||||
|
.build();
|
||||||
|
// 创建监听容器
|
||||||
|
StreamMessageListenerContainer<String, MapRecord<String, String, String>> container = StreamMessageListenerContainer
|
||||||
|
.create(redisTemplate.getRequiredConnectionFactory(), streamMessageListenerContainerOptions);
|
||||||
|
//创建消费者组
|
||||||
|
try {
|
||||||
|
redisTemplate.opsForStream().createGroup(PublishStreamName, PublishConsumerGroup);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.info("消费者组:{} 已存在", PublishConsumerGroup);
|
||||||
|
}
|
||||||
|
// 添加消费者
|
||||||
|
for (int i = 0; i < properties.getConsumerCount(); i++) {
|
||||||
|
Consumer consumer = Consumer.from(PublishConsumerGroup, "cms-publish-consumer-" + i);
|
||||||
|
PublishTaskReceiver publishTaskReceiver = new PublishTaskReceiver(cmsStaticizeService, redisTemplate);
|
||||||
|
publishTaskReceiver.setConsumer(consumer);
|
||||||
|
container.receive(consumer, StreamOffset.create(PublishStreamName, ReadOffset.lastConsumed()), publishTaskReceiver);
|
||||||
|
}
|
||||||
|
container.start();
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ThreadPoolTaskExecutor cmsPublishThreadPoolTaskExecutor() {
|
||||||
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
|
executor.setThreadNamePrefix(properties.getPool().getThreadNamePrefix());
|
||||||
|
executor.setCorePoolSize(properties.getPool().getCoreSize());
|
||||||
|
executor.setQueueCapacity(properties.getPool().getQueueCapacity());
|
||||||
|
executor.setMaxPoolSize(properties.getPool().getMaxSize());
|
||||||
|
executor.setKeepAliveSeconds((int) properties.getPool().getKeepAlive().getSeconds());
|
||||||
|
executor.setAllowCoreThreadTimeOut(this.properties.getPool().isAllowCoreThreadTimeout());
|
||||||
|
executor.setWaitForTasksToCompleteOnShutdown(properties.getShutdown().isAwaitTermination());
|
||||||
|
executor.setAwaitTerminationSeconds((int) properties.getShutdown().getAwaitTerminationPeriod().toSeconds());
|
||||||
|
log.info("Cms publish task executor initialize: {}", executor.getThreadNamePrefix());
|
||||||
|
executor.initialize();
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
package com.chestnut.contentcore.publish.strategies;
|
||||||
|
|
||||||
|
import com.chestnut.contentcore.config.properties.CMSPublishProperties;
|
||||||
|
import com.chestnut.contentcore.publish.CmsStaticizeService;
|
||||||
|
import com.chestnut.contentcore.publish.IPublishStrategy;
|
||||||
|
import com.chestnut.contentcore.publish.IStaticizeType;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布策略:ThreadPool
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@ConditionalOnProperty(prefix = CMSPublishProperties.PREFIX, name = "strategy", havingValue = ThreadPoolPublishStrategy.ID)
|
||||||
|
public class ThreadPoolPublishStrategy implements IPublishStrategy, CommandLineRunner {
|
||||||
|
|
||||||
|
public static final String ID = "ThreadPool";
|
||||||
|
|
||||||
|
private final CMSPublishProperties properties;
|
||||||
|
|
||||||
|
private final CmsStaticizeService cmsStaticizeService;
|
||||||
|
|
||||||
|
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publish(String dataType, String dataId) {
|
||||||
|
IStaticizeType staticizeType = cmsStaticizeService.getStaticizeType(dataType);
|
||||||
|
if (Objects.nonNull(staticizeType)) {
|
||||||
|
threadPoolTaskExecutor.execute(() -> {
|
||||||
|
staticizeType.staticize(dataId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTaskCount() {
|
||||||
|
// 返回线程池队列信息
|
||||||
|
return threadPoolTaskExecutor.getQueueSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanTasks() {
|
||||||
|
// 清空线程池队列
|
||||||
|
threadPoolTaskExecutor.getThreadPoolExecutor().getQueue().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(String... args) throws Exception {
|
||||||
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
|
executor.setThreadNamePrefix(properties.getPool().getThreadNamePrefix());
|
||||||
|
executor.setCorePoolSize(properties.getPool().getCoreSize());
|
||||||
|
executor.setQueueCapacity(properties.getPool().getQueueCapacity());
|
||||||
|
executor.setMaxPoolSize(properties.getPool().getMaxSize());
|
||||||
|
executor.setKeepAliveSeconds((int) properties.getPool().getKeepAlive().getSeconds());
|
||||||
|
executor.setAllowCoreThreadTimeOut(this.properties.getPool().isAllowCoreThreadTimeout());
|
||||||
|
executor.setWaitForTasksToCompleteOnShutdown(properties.getShutdown().isAwaitTermination());
|
||||||
|
executor.setAwaitTerminationSeconds((int) properties.getShutdown().getAwaitTerminationPeriod().toSeconds());
|
||||||
|
executor.initialize();
|
||||||
|
this.threadPoolTaskExecutor = executor;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -26,6 +26,8 @@ import com.chestnut.contentcore.domain.dto.*;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public interface ICatalogService extends IService<CmsCatalog> {
|
public interface ICatalogService extends IService<CmsCatalog> {
|
||||||
|
|
||||||
@ -52,7 +54,7 @@ public interface ICatalogService extends IService<CmsCatalog> {
|
|||||||
* @param catalogs
|
* @param catalogs
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
List<TreeNode<String>> buildCatalogTreeData(List<CmsCatalog> catalogs);
|
List<TreeNode<String>> buildCatalogTreeData(List<CmsCatalog> catalogs, BiConsumer<CmsCatalog, TreeNode<String>> consumer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验栏目别名、目录是否重复
|
* 校验栏目别名、目录是否重复
|
||||||
|
|||||||
@ -63,6 +63,7 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@ -129,7 +130,7 @@ public class CatalogServiceImpl extends ServiceImpl<CmsCatalogMapper, CmsCatalog
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<TreeNode<String>> buildCatalogTreeData(List<CmsCatalog> catalogs) {
|
public List<TreeNode<String>> buildCatalogTreeData(List<CmsCatalog> catalogs, BiConsumer<CmsCatalog, TreeNode<String>> consumer) {
|
||||||
if (Objects.isNull(catalogs)) {
|
if (Objects.isNull(catalogs)) {
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
@ -142,6 +143,7 @@ public class CatalogServiceImpl extends ServiceImpl<CmsCatalogMapper, CmsCatalog
|
|||||||
c.getLogo() == null ? "" : c.getLogo(), "logoSrc", logoSrc == null ? "" : logoSrc, "description",
|
c.getLogo() == null ? "" : c.getLogo(), "logoSrc", logoSrc == null ? "" : logoSrc, "description",
|
||||||
c.getDescription() == null ? "" : c.getDescription());
|
c.getDescription() == null ? "" : c.getDescription());
|
||||||
treeNode.setProps(props);
|
treeNode.setProps(props);
|
||||||
|
consumer.accept(c, treeNode);
|
||||||
return treeNode;
|
return treeNode;
|
||||||
}).toList();
|
}).toList();
|
||||||
return TreeNode.build(list);
|
return TreeNode.build(list);
|
||||||
|
|||||||
@ -91,7 +91,7 @@ public class DynamicPageService {
|
|||||||
// init template datamode
|
// init template datamode
|
||||||
TemplateUtils.initGlobalVariables(site, templateContext);
|
TemplateUtils.initGlobalVariables(site, templateContext);
|
||||||
// init templateType data to datamode
|
// init templateType data to datamode
|
||||||
templateContext.getVariables().put("Request", ServletUtils.getParameters());
|
templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, ServletUtils.getParameters());
|
||||||
dpt.initTemplateData(parameters, templateContext);
|
dpt.initTemplateData(parameters, templateContext);
|
||||||
// staticize
|
// staticize
|
||||||
this.staticizeService.process(templateContext, response.getWriter());
|
this.staticizeService.process(templateContext, response.getWriter());
|
||||||
|
|||||||
@ -35,9 +35,10 @@ import com.chestnut.contentcore.enums.ContentCopyType;
|
|||||||
import com.chestnut.contentcore.exception.ContentCoreErrorCode;
|
import com.chestnut.contentcore.exception.ContentCoreErrorCode;
|
||||||
import com.chestnut.contentcore.listener.event.AfterContentPublishEvent;
|
import com.chestnut.contentcore.listener.event.AfterContentPublishEvent;
|
||||||
import com.chestnut.contentcore.properties.MaxPageOnContentPublishProperty;
|
import com.chestnut.contentcore.properties.MaxPageOnContentPublishProperty;
|
||||||
import com.chestnut.contentcore.publish.CatalogPublishTask;
|
import com.chestnut.contentcore.publish.staticize.CatalogStaticizeType;
|
||||||
import com.chestnut.contentcore.publish.ContentPublishTask;
|
import com.chestnut.contentcore.publish.staticize.ContentStaticizeType;
|
||||||
import com.chestnut.contentcore.publish.SitePublishTask;
|
import com.chestnut.contentcore.publish.staticize.SiteStaticizeType;
|
||||||
|
import com.chestnut.contentcore.publish.IPublishStrategy;
|
||||||
import com.chestnut.contentcore.service.*;
|
import com.chestnut.contentcore.service.*;
|
||||||
import com.chestnut.contentcore.template.ITemplateType;
|
import com.chestnut.contentcore.template.ITemplateType;
|
||||||
import com.chestnut.contentcore.template.impl.CatalogTemplateType;
|
import com.chestnut.contentcore.template.impl.CatalogTemplateType;
|
||||||
@ -80,11 +81,7 @@ public class PublishServiceImpl implements IPublishService, ApplicationContextAw
|
|||||||
|
|
||||||
private final AsyncTaskManager asyncTaskManager;
|
private final AsyncTaskManager asyncTaskManager;
|
||||||
|
|
||||||
private final SitePublishTask sitePublishTask;
|
private final IPublishStrategy publishStrategy;
|
||||||
|
|
||||||
private final CatalogPublishTask catalogPublishTask;
|
|
||||||
|
|
||||||
private final ContentPublishTask contentPublishTask;
|
|
||||||
|
|
||||||
private ApplicationContext applicationContext;
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
@ -191,7 +188,7 @@ public class PublishServiceImpl implements IPublishService, ApplicationContextAw
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void asyncPublishSite(CmsSite site) {
|
private void asyncPublishSite(CmsSite site) {
|
||||||
sitePublishTask.publish(site);
|
publishStrategy.publish(SiteStaticizeType.TYPE, site.getSiteId().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -361,7 +358,7 @@ public class PublishServiceImpl implements IPublishService, ApplicationContextAw
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void asyncPublishCatalog(final CmsCatalog catalog) {
|
public void asyncPublishCatalog(final CmsCatalog catalog) {
|
||||||
catalogPublishTask.publish(catalog);
|
publishStrategy.publish(CatalogStaticizeType.TYPE, catalog.getCatalogId().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -572,14 +569,14 @@ public class PublishServiceImpl implements IPublishService, ApplicationContextAw
|
|||||||
if (publishPipeCodes.isEmpty()) {
|
if (publishPipeCodes.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
contentPublishTask.publish(content.getContentEntity());
|
publishStrategy.publish(ContentStaticizeType.TYPE, content.getContentEntity().getContentId().toString());
|
||||||
// 关联内容静态化,映射的引用内容
|
// 关联内容静态化,映射的引用内容
|
||||||
LambdaQueryWrapper<CmsContent> q = new LambdaQueryWrapper<CmsContent>()
|
LambdaQueryWrapper<CmsContent> q = new LambdaQueryWrapper<CmsContent>()
|
||||||
.eq(CmsContent::getCopyId, content.getContentEntity().getContentId())
|
.eq(CmsContent::getCopyId, content.getContentEntity().getContentId())
|
||||||
.eq(CmsContent::getCopyType, ContentCopyType.Mapping);
|
.eq(CmsContent::getCopyType, ContentCopyType.Mapping);
|
||||||
List<CmsContent> mappingContents = contentService.list(q);
|
List<CmsContent> mappingContents = contentService.list(q);
|
||||||
for (CmsContent mappingContent : mappingContents) {
|
for (CmsContent mappingContent : mappingContents) {
|
||||||
contentPublishTask.publish(mappingContent);
|
publishStrategy.publish(ContentStaticizeType.TYPE, mappingContent.getContentId().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -112,7 +112,6 @@ public class TemplateServiceImpl extends ServiceImpl<CmsTemplateMapper, CmsTempl
|
|||||||
.filter(t -> t.getPublishPipeCode().equals(pp.getCode()) && t.getPath().equals(path))
|
.filter(t -> t.getPublishPipeCode().equals(pp.getCode()) && t.getPath().equals(path))
|
||||||
.findFirst();
|
.findFirst();
|
||||||
opt.ifPresentOrElse(t -> {
|
opt.ifPresentOrElse(t -> {
|
||||||
System.out.println("scan template: " + file.getName() + "|" + file.lastModified() + " = " + t.getModifyTime());
|
|
||||||
if (t.getModifyTime() != file.lastModified()) {
|
if (t.getModifyTime() != file.lastModified()) {
|
||||||
try {
|
try {
|
||||||
t.setFilesize(file.length());
|
t.setFilesize(file.length());
|
||||||
|
|||||||
@ -29,10 +29,9 @@ import com.chestnut.contentcore.service.ITemplateService;
|
|||||||
import com.chestnut.contentcore.util.SiteUtils;
|
import com.chestnut.contentcore.util.SiteUtils;
|
||||||
import com.chestnut.contentcore.util.TemplateUtils;
|
import com.chestnut.contentcore.util.TemplateUtils;
|
||||||
import freemarker.core.Environment;
|
import freemarker.core.Environment;
|
||||||
import freemarker.template.Template;
|
import freemarker.template.*;
|
||||||
import freemarker.template.TemplateException;
|
|
||||||
import freemarker.template.TemplateModel;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.collections4.MapUtils;
|
import org.apache.commons.collections4.MapUtils;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@ -47,6 +46,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Component
|
@Component
|
||||||
public class CmsIncludeTag extends AbstractTag {
|
public class CmsIncludeTag extends AbstractTag {
|
||||||
@ -128,7 +128,11 @@ public class CmsIncludeTag extends AbstractTag {
|
|||||||
if (context.isPreview()) {
|
if (context.isPreview()) {
|
||||||
Template includeTemplate = env.getTemplateForInclusion(includeTemplateKey,
|
Template includeTemplate = env.getTemplateForInclusion(includeTemplateKey,
|
||||||
StandardCharsets.UTF_8.displayName(), true);
|
StandardCharsets.UTF_8.displayName(), true);
|
||||||
env.setVariable("IncludeRequest", wrap(env, StringUtils.splitToMap(params, "&", "=")));
|
Map<String, String> paramsMap = StringUtils.splitToMap(params, "&", "=");
|
||||||
|
Map<String, String> mergeParams = mergeRequestVariable(env, paramsMap);
|
||||||
|
env.setVariable(TemplateUtils.TemplateVariable_Request, wrap(env, mergeParams));
|
||||||
|
// TODO 兼容历史版本,下个大版本移除IncludeRequest模板变量
|
||||||
|
env.setVariable("IncludeRequest", wrap(env, mergeParams));
|
||||||
env.include(includeTemplate);
|
env.include(includeTemplate);
|
||||||
} else if (virtual) {
|
} else if (virtual) {
|
||||||
// 动态模板
|
// 动态模板
|
||||||
@ -159,6 +163,23 @@ public class CmsIncludeTag extends AbstractTag {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<String, String> mergeRequestVariable(Environment env, Map<String, String> params) throws TemplateModelException {
|
||||||
|
TemplateModel variable = env.getVariable(TemplateUtils.TemplateVariable_Request);
|
||||||
|
if (Objects.nonNull(variable)) {
|
||||||
|
if (variable instanceof TemplateHashModelEx2 req) {
|
||||||
|
for (TemplateHashModelEx2.KeyValuePairIterator iterator = req.keyValuePairIterator();iterator.hasNext();) {
|
||||||
|
TemplateHashModelEx2.KeyValuePair next = iterator.next();
|
||||||
|
String key = ((SimpleScalar) next.getKey()).getAsString();
|
||||||
|
if (params.containsKey(key)) {
|
||||||
|
log.warn("<@cms_include> file parameter `{}` conflicts with the Request parameter.", key);
|
||||||
|
}
|
||||||
|
params.put(key, ((SimpleScalar) next.getValue()).getAsString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成包含模板静态化内容
|
* 生成包含模板静态化内容
|
||||||
*/
|
*/
|
||||||
@ -169,7 +190,10 @@ public class CmsIncludeTag extends AbstractTag {
|
|||||||
env.setOut(writer);
|
env.setOut(writer);
|
||||||
Template includeTemplate = env.getTemplateForInclusion(includeTemplateName,
|
Template includeTemplate = env.getTemplateForInclusion(includeTemplateName,
|
||||||
StandardCharsets.UTF_8.displayName(), true);
|
StandardCharsets.UTF_8.displayName(), true);
|
||||||
env.setVariable("IncludeRequest", wrap(env, params));
|
Map<String, String> mergeParams = mergeRequestVariable(env, params);
|
||||||
|
env.setVariable(TemplateUtils.TemplateVariable_Request, wrap(env, mergeParams));
|
||||||
|
// TODO 兼容历史版本,下个大版本移除IncludeRequest模板变量
|
||||||
|
env.setVariable("IncludeRequest", wrap(env, mergeParams));
|
||||||
env.include(includeTemplate);
|
env.include(includeTemplate);
|
||||||
return writer.getBuffer().toString();
|
return writer.getBuffer().toString();
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@ -38,6 +38,16 @@ import java.util.Map;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class TemplateUtils {
|
public class TemplateUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模板变量:请求参数
|
||||||
|
*/
|
||||||
|
public final static String TemplateVariable_Request = "Request";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模板变量:<@cms_include>标签file属性请求参数
|
||||||
|
*/
|
||||||
|
public final static String TemplateVariable_IncludeRequest = "IncludeRequest";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模板变量:预览模式登录用户token键名
|
* 模板变量:预览模式登录用户token键名
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
package com.chestnut.cms.dynamic.core.impl;
|
package com.chestnut.cms.dynamic.core.impl;
|
||||||
|
|
||||||
import com.chestnut.cms.dynamic.core.IDynamicPageInitData;
|
import com.chestnut.cms.dynamic.core.IDynamicPageInitData;
|
||||||
|
import com.chestnut.cms.member.CmsMemberConstants;
|
||||||
import com.chestnut.common.security.domain.LoginUser;
|
import com.chestnut.common.security.domain.LoginUser;
|
||||||
import com.chestnut.common.staticize.core.TemplateContext;
|
import com.chestnut.common.staticize.core.TemplateContext;
|
||||||
import com.chestnut.member.security.StpMemberUtil;
|
import com.chestnut.member.security.StpMemberUtil;
|
||||||
@ -49,8 +50,9 @@ public class MemberDynamicPageInitData implements IDynamicPageInitData {
|
|||||||
public void initTemplateData(TemplateContext context, Map<String, String> parameters) {
|
public void initTemplateData(TemplateContext context, Map<String, String> parameters) {
|
||||||
if (StpMemberUtil.isLogin()) {
|
if (StpMemberUtil.isLogin()) {
|
||||||
LoginUser loginUser = StpMemberUtil.getLoginUser();
|
LoginUser loginUser = StpMemberUtil.getLoginUser();
|
||||||
context.getVariables().put("Member", loginUser.getUser());
|
context.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER, loginUser.getUser());
|
||||||
context.getVariables().put("MemberResourcePrefix", MemberUtils.getMemberResourcePrefix(context.isPreview()));
|
context.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER_RESOURCE_PREFIX,
|
||||||
|
MemberUtils.getMemberResourcePrefix(context.isPreview()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -200,7 +200,7 @@ public class DynamicPageServiceImpl extends ServiceImpl<CmsDynamicPageMapper, Cm
|
|||||||
// init template datamode
|
// init template datamode
|
||||||
TemplateUtils.initGlobalVariables(site, templateContext);
|
TemplateUtils.initGlobalVariables(site, templateContext);
|
||||||
// init templateType data to datamode
|
// init templateType data to datamode
|
||||||
templateContext.getVariables().put("Request", parameters);
|
templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, parameters);
|
||||||
// 动态页面自定义数据
|
// 动态页面自定义数据
|
||||||
if (Objects.nonNull(dynamicPage.getInitDataTypes())) {
|
if (Objects.nonNull(dynamicPage.getInitDataTypes())) {
|
||||||
dynamicPage.getInitDataTypes().forEach(initDataType -> {
|
dynamicPage.getInitDataTypes().forEach(initDataType -> {
|
||||||
|
|||||||
@ -26,4 +26,14 @@ public interface CmsMemberConstants {
|
|||||||
String MEMBER_FAVORITES_DATA_TYPE = "cms_content";
|
String MEMBER_FAVORITES_DATA_TYPE = "cms_content";
|
||||||
|
|
||||||
String MEMBER_LIKE_DATA_TYPE = "cms_content";
|
String MEMBER_LIKE_DATA_TYPE = "cms_content";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模板变量:会员信息
|
||||||
|
*/
|
||||||
|
String TEMPLATE_VARIABLE_MEMBER = "Member";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模板变量:会员资源前缀
|
||||||
|
*/
|
||||||
|
String TEMPLATE_VARIABLE_MEMBER_RESOURCE_PREFIX = "MemberResourcePrefix";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,10 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package com.chestnut.cms.member.impl;
|
package com.chestnut.cms.member.impl;
|
||||||
|
|
||||||
|
import com.chestnut.cms.member.CmsMemberConstants;
|
||||||
import com.chestnut.cms.member.publishpipe.PublishPipeProp_MemberBindEmailTemplate;
|
import com.chestnut.cms.member.publishpipe.PublishPipeProp_MemberBindEmailTemplate;
|
||||||
import com.chestnut.common.staticize.core.TemplateContext;
|
import com.chestnut.common.staticize.core.TemplateContext;
|
||||||
import com.chestnut.common.utils.ServletUtils;
|
import com.chestnut.common.utils.ServletUtils;
|
||||||
import com.chestnut.contentcore.core.IDynamicPageType;
|
import com.chestnut.contentcore.core.IDynamicPageType;
|
||||||
|
import com.chestnut.contentcore.util.TemplateUtils;
|
||||||
import com.chestnut.member.domain.Member;
|
import com.chestnut.member.domain.Member;
|
||||||
import com.chestnut.member.domain.vo.MemberCache;
|
import com.chestnut.member.domain.vo.MemberCache;
|
||||||
import com.chestnut.member.service.IMemberService;
|
import com.chestnut.member.service.IMemberService;
|
||||||
@ -95,8 +97,9 @@ public class AccountBindEmailDynamicPageType implements IDynamicPageType {
|
|||||||
public void initTemplateData(Map<String, String> parameters, TemplateContext templateContext) {
|
public void initTemplateData(Map<String, String> parameters, TemplateContext templateContext) {
|
||||||
Long memberId = MapUtils.getLong(parameters, "memberId");
|
Long memberId = MapUtils.getLong(parameters, "memberId");
|
||||||
Member member = this.memberService.getById(memberId);
|
Member member = this.memberService.getById(memberId);
|
||||||
templateContext.getVariables().put("Member", member);
|
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER, member);
|
||||||
templateContext.getVariables().put("MemberResourcePrefix", MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER_RESOURCE_PREFIX,
|
||||||
templateContext.getVariables().put("Request", parameters);
|
MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
||||||
|
templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, parameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,9 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package com.chestnut.cms.member.impl;
|
package com.chestnut.cms.member.impl;
|
||||||
|
|
||||||
|
import com.chestnut.cms.member.CmsMemberConstants;
|
||||||
import com.chestnut.cms.member.publishpipe.PublishPipeProp_AccountCentreTemplate;
|
import com.chestnut.cms.member.publishpipe.PublishPipeProp_AccountCentreTemplate;
|
||||||
import com.chestnut.common.staticize.core.TemplateContext;
|
import com.chestnut.common.staticize.core.TemplateContext;
|
||||||
import com.chestnut.contentcore.core.IDynamicPageType;
|
import com.chestnut.contentcore.core.IDynamicPageType;
|
||||||
|
import com.chestnut.contentcore.util.TemplateUtils;
|
||||||
import com.chestnut.member.domain.vo.MemberCache;
|
import com.chestnut.member.domain.vo.MemberCache;
|
||||||
import com.chestnut.member.service.IMemberStatDataService;
|
import com.chestnut.member.service.IMemberStatDataService;
|
||||||
import com.chestnut.member.util.MemberUtils;
|
import com.chestnut.member.util.MemberUtils;
|
||||||
@ -92,9 +94,10 @@ public class AccountCentreDynamicPageType implements IDynamicPageType {
|
|||||||
Long siteId = MapUtils.getLong(parameters, "sid");
|
Long siteId = MapUtils.getLong(parameters, "sid");
|
||||||
Long memberId = MapUtils.getLong(parameters, "memberId");
|
Long memberId = MapUtils.getLong(parameters, "memberId");
|
||||||
MemberCache member = this.memberStatDataService.getMemberCache(memberId);
|
MemberCache member = this.memberStatDataService.getMemberCache(memberId);
|
||||||
templateContext.getVariables().put("Member", member);
|
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER, member);
|
||||||
templateContext.getVariables().put("MemberResourcePrefix", MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER_RESOURCE_PREFIX,
|
||||||
templateContext.getVariables().put("Request", parameters);
|
MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
||||||
|
templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, parameters);
|
||||||
templateContext.setPageIndex(MapUtils.getIntValue(parameters, "page", 1));
|
templateContext.setPageIndex(MapUtils.getIntValue(parameters, "page", 1));
|
||||||
|
|
||||||
String link = "account/" + memberId + "?type=" + parameters.get("type");
|
String link = "account/" + memberId + "?type=" + parameters.get("type");
|
||||||
|
|||||||
@ -17,6 +17,7 @@ package com.chestnut.cms.member.impl;
|
|||||||
|
|
||||||
import com.chestnut.article.domain.CmsArticleDetail;
|
import com.chestnut.article.domain.CmsArticleDetail;
|
||||||
import com.chestnut.article.service.IArticleService;
|
import com.chestnut.article.service.IArticleService;
|
||||||
|
import com.chestnut.cms.member.CmsMemberConstants;
|
||||||
import com.chestnut.cms.member.domain.vo.ContributeArticleVO;
|
import com.chestnut.cms.member.domain.vo.ContributeArticleVO;
|
||||||
import com.chestnut.cms.member.publishpipe.PublishPipeProp_MemberContributeTemplate;
|
import com.chestnut.cms.member.publishpipe.PublishPipeProp_MemberContributeTemplate;
|
||||||
import com.chestnut.common.staticize.core.TemplateContext;
|
import com.chestnut.common.staticize.core.TemplateContext;
|
||||||
@ -27,6 +28,7 @@ import com.chestnut.contentcore.core.IDynamicPageType;
|
|||||||
import com.chestnut.contentcore.domain.CmsContent;
|
import com.chestnut.contentcore.domain.CmsContent;
|
||||||
import com.chestnut.contentcore.service.IContentService;
|
import com.chestnut.contentcore.service.IContentService;
|
||||||
import com.chestnut.contentcore.util.InternalUrlUtils;
|
import com.chestnut.contentcore.util.InternalUrlUtils;
|
||||||
|
import com.chestnut.contentcore.util.TemplateUtils;
|
||||||
import com.chestnut.member.domain.Member;
|
import com.chestnut.member.domain.Member;
|
||||||
import com.chestnut.member.domain.vo.MemberCache;
|
import com.chestnut.member.domain.vo.MemberCache;
|
||||||
import com.chestnut.member.service.IMemberService;
|
import com.chestnut.member.service.IMemberService;
|
||||||
@ -108,9 +110,10 @@ public class AccountContributeDynamicPageType implements IDynamicPageType {
|
|||||||
public void initTemplateData(Map<String, String> parameters, TemplateContext templateContext) {
|
public void initTemplateData(Map<String, String> parameters, TemplateContext templateContext) {
|
||||||
Long memberId = MapUtils.getLong(parameters, "memberId");
|
Long memberId = MapUtils.getLong(parameters, "memberId");
|
||||||
Member member = this.memberService.getById(memberId);
|
Member member = this.memberService.getById(memberId);
|
||||||
templateContext.getVariables().put("Member", member);
|
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER, member);
|
||||||
templateContext.getVariables().put("MemberResourcePrefix", MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER_RESOURCE_PREFIX,
|
||||||
templateContext.getVariables().put("Request", parameters);
|
MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
||||||
|
templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, parameters);
|
||||||
|
|
||||||
Long contentId = MapUtils.getLong(parameters, "cid", 0L);
|
Long contentId = MapUtils.getLong(parameters, "cid", 0L);
|
||||||
if (IdUtils.validate(contentId)) {
|
if (IdUtils.validate(contentId)) {
|
||||||
|
|||||||
@ -15,10 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package com.chestnut.cms.member.impl;
|
package com.chestnut.cms.member.impl;
|
||||||
|
|
||||||
|
import com.chestnut.cms.member.CmsMemberConstants;
|
||||||
import com.chestnut.cms.member.publishpipe.PublishPipeProp_MemberPasswordTemplate;
|
import com.chestnut.cms.member.publishpipe.PublishPipeProp_MemberPasswordTemplate;
|
||||||
import com.chestnut.common.staticize.core.TemplateContext;
|
import com.chestnut.common.staticize.core.TemplateContext;
|
||||||
import com.chestnut.common.utils.ServletUtils;
|
import com.chestnut.common.utils.ServletUtils;
|
||||||
import com.chestnut.contentcore.core.IDynamicPageType;
|
import com.chestnut.contentcore.core.IDynamicPageType;
|
||||||
|
import com.chestnut.contentcore.util.TemplateUtils;
|
||||||
import com.chestnut.member.domain.Member;
|
import com.chestnut.member.domain.Member;
|
||||||
import com.chestnut.member.domain.vo.MemberCache;
|
import com.chestnut.member.domain.vo.MemberCache;
|
||||||
import com.chestnut.member.service.IMemberService;
|
import com.chestnut.member.service.IMemberService;
|
||||||
@ -95,8 +97,9 @@ public class AccountPasswordDynamicPageType implements IDynamicPageType {
|
|||||||
public void initTemplateData(Map<String, String> parameters, TemplateContext templateContext) {
|
public void initTemplateData(Map<String, String> parameters, TemplateContext templateContext) {
|
||||||
Long memberId = MapUtils.getLong(parameters, "memberId");
|
Long memberId = MapUtils.getLong(parameters, "memberId");
|
||||||
Member member = this.memberService.getById(memberId);
|
Member member = this.memberService.getById(memberId);
|
||||||
templateContext.getVariables().put("Member", member);
|
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER, member);
|
||||||
templateContext.getVariables().put("MemberResourcePrefix", MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER_RESOURCE_PREFIX,
|
||||||
templateContext.getVariables().put("Request", ServletUtils.getParameters());
|
MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
||||||
|
templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, ServletUtils.getParameters());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,10 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package com.chestnut.cms.member.impl;
|
package com.chestnut.cms.member.impl;
|
||||||
|
|
||||||
|
import com.chestnut.cms.member.CmsMemberConstants;
|
||||||
import com.chestnut.cms.member.publishpipe.PublishPipeProp_MemberSettingTemplate;
|
import com.chestnut.cms.member.publishpipe.PublishPipeProp_MemberSettingTemplate;
|
||||||
import com.chestnut.common.staticize.core.TemplateContext;
|
import com.chestnut.common.staticize.core.TemplateContext;
|
||||||
import com.chestnut.common.utils.ServletUtils;
|
import com.chestnut.common.utils.ServletUtils;
|
||||||
import com.chestnut.contentcore.core.IDynamicPageType;
|
import com.chestnut.contentcore.core.IDynamicPageType;
|
||||||
|
import com.chestnut.contentcore.util.TemplateUtils;
|
||||||
import com.chestnut.member.domain.Member;
|
import com.chestnut.member.domain.Member;
|
||||||
import com.chestnut.member.domain.vo.MemberCache;
|
import com.chestnut.member.domain.vo.MemberCache;
|
||||||
import com.chestnut.member.service.IMemberService;
|
import com.chestnut.member.service.IMemberService;
|
||||||
@ -95,8 +97,9 @@ public class AccountSettingDynamicPageType implements IDynamicPageType {
|
|||||||
public void initTemplateData(Map<String, String> parameters, TemplateContext templateContext) {
|
public void initTemplateData(Map<String, String> parameters, TemplateContext templateContext) {
|
||||||
Long memberId = MapUtils.getLong(parameters, "memberId");
|
Long memberId = MapUtils.getLong(parameters, "memberId");
|
||||||
Member member = this.memberService.getById(memberId);
|
Member member = this.memberService.getById(memberId);
|
||||||
templateContext.getVariables().put("Member", member);
|
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER, member);
|
||||||
templateContext.getVariables().put("MemberResourcePrefix", MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER_RESOURCE_PREFIX,
|
||||||
templateContext.getVariables().put("Request", parameters);
|
MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
||||||
|
templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, parameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -78,7 +78,7 @@ public class SearchDynamicPageType implements IDynamicPageType {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initTemplateData(Map<String, String> parameters, TemplateContext templateContext) {
|
public void initTemplateData(Map<String, String> parameters, TemplateContext templateContext) {
|
||||||
templateContext.getVariables().put("Request", ServletUtils.getParameters());
|
templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, ServletUtils.getParameters());
|
||||||
String link = "_search?q=" + parameters.get("q");
|
String link = "_search?q=" + parameters.get("q");
|
||||||
if (templateContext.isPreview()) {
|
if (templateContext.isPreview()) {
|
||||||
link += "&sid=" + parameters.get("sid") + "&pp=" + templateContext.getPublishPipeCode() + "&preview=true";
|
link += "&sid=" + parameters.get("sid") + "&pp=" + templateContext.getPublishPipeCode() + "&preview=true";
|
||||||
|
|||||||
@ -15,6 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package com.chestnut.common.domain;
|
package com.chestnut.common.domain;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -22,13 +27,14 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 树结构节点实体类
|
* 树结构节点实体类
|
||||||
*/
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
public class TreeNode<T> implements Serializable {
|
public class TreeNode<T> implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/** 节点ID */
|
/** 节点ID */
|
||||||
@ -48,7 +54,7 @@ public class TreeNode<T> implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* 是否禁用
|
* 是否禁用
|
||||||
*/
|
*/
|
||||||
private boolean isDisabled = false;
|
private boolean disabled = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否新节点,会不同颜色显示
|
* 是否新节点,会不同颜色显示
|
||||||
@ -73,10 +79,6 @@ public class TreeNode<T> implements Serializable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建树结构
|
* 构建树结构
|
||||||
*
|
|
||||||
* @param <T>
|
|
||||||
* @param list
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
public static <T> List<TreeNode<T>> build(List<TreeNode<T>> list) {
|
public static <T> List<TreeNode<T>> build(List<TreeNode<T>> list) {
|
||||||
Map<T, List<TreeNode<T>>> mapChildren = list.stream().filter(n -> !n.isRoot)
|
Map<T, List<TreeNode<T>>> mapChildren = list.stream().filter(n -> !n.isRoot)
|
||||||
@ -98,86 +100,10 @@ public class TreeNode<T> implements Serializable {
|
|||||||
this.isRoot = isRoot;
|
this.isRoot = isRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(T id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T getParentId() {
|
|
||||||
return parentId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setParentId(T parentId) {
|
|
||||||
this.parentId = parentId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLabel() {
|
|
||||||
return label;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLabel(String label) {
|
|
||||||
this.label = label;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<TreeNode<T>> getChildren() {
|
|
||||||
return children;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setChildren(List<TreeNode<T>> children) {
|
|
||||||
this.children = children;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getIsRoot() {
|
|
||||||
return isRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIsRoot(boolean isRoot) {
|
|
||||||
this.isRoot = isRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getIsDisabled() {
|
|
||||||
return isDisabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDisabled(boolean isDisabled) {
|
|
||||||
this.isDisabled = isDisabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getIsNew() {
|
|
||||||
return isNew;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNew(boolean isNew) {
|
|
||||||
this.isNew = isNew;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getIsDefaultExpanded() {
|
|
||||||
return isDefaultExpanded;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDefaultExpanded(boolean isDefaultExpanded) {
|
|
||||||
this.isDefaultExpanded = isDefaultExpanded;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, Object> getProps() {
|
public Map<String, Object> getProps() {
|
||||||
if (props == null) {
|
if (props == null) {
|
||||||
props = new HashMap<>();
|
props = new HashMap<>();
|
||||||
}
|
}
|
||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProps(Map<String, Object> props) {
|
|
||||||
this.props = props;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isChecked() {
|
|
||||||
return checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setChecked(boolean checked) {
|
|
||||||
this.checked = checked;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,12 +20,27 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ChineseSpelling {
|
public class ChineseSpelling {
|
||||||
|
|
||||||
private static LinkedHashMap<String, String> specialFamilyNames = new LinkedHashMap<>();
|
private static LinkedHashMap<String, String> specialFamilyNames = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
public static int countChineseCharactersByRegex(String input) {
|
||||||
|
// 使用正则表达式匹配中文字符
|
||||||
|
String regex = "[\\u4e00-\\u9fa5]";
|
||||||
|
Pattern pattern = Pattern.compile(regex);
|
||||||
|
Matcher matcher = pattern.matcher(input);
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
while (matcher.find()) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得单个汉字的GBK编码
|
* 获得单个汉字的GBK编码
|
||||||
*
|
*
|
||||||
|
|||||||
@ -20,7 +20,6 @@ import org.springframework.util.AntPathMatcher;
|
|||||||
|
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -743,9 +742,6 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static Map<String, String> getPathParameterMap(String path) {
|
public static Map<String, String> getPathParameterMap(String path) {
|
||||||
if (Objects.isNull(path) || !path.contains("?")) {
|
|
||||||
return Map.of();
|
|
||||||
}
|
|
||||||
String str = substringAfter(path, "?");
|
String str = substringAfter(path, "?");
|
||||||
return splitToMap(str, "&", "=");
|
return splitToMap(str, "&", "=");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -199,12 +199,12 @@ public class FreeMarkerUtils {
|
|||||||
|
|
||||||
private static TemplateModel evalTemplateModel(Environment env, String[] names) throws TemplateModelException {
|
private static TemplateModel evalTemplateModel(Environment env, String[] names) throws TemplateModelException {
|
||||||
TemplateModel model = env.getVariable(names[0]);
|
TemplateModel model = env.getVariable(names[0]);
|
||||||
if (model == null || !(model instanceof TemplateHashModel)) {
|
if (!(model instanceof TemplateHashModel)) {
|
||||||
throw new TemplateModelException();
|
throw new TemplateModelException();
|
||||||
}
|
}
|
||||||
for (int i = 1; i < names.length - 1; i++) {
|
for (int i = 1; i < names.length - 1; i++) {
|
||||||
model = ((TemplateHashModel) model).get(names[i]);
|
model = ((TemplateHashModel) model).get(names[i]);
|
||||||
if (model == null || !(model instanceof TemplateHashModel)) {
|
if (!(model instanceof TemplateHashModel)) {
|
||||||
throw new TemplateModelException();
|
throw new TemplateModelException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,40 @@
|
|||||||
|
package com.chestnut.member.core;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MemberPrivilegeProvider
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
public interface MemberPrivType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定类型权限拥有者的权限列表
|
||||||
|
*
|
||||||
|
* @param ownerType 权限所有者类型
|
||||||
|
* @param owner 权限所有者唯一标识
|
||||||
|
* @return 权限列表
|
||||||
|
*/
|
||||||
|
default Set<String> getPrivileges(String ownerType, String owner) {
|
||||||
|
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存权限数据
|
||||||
|
*/
|
||||||
|
default void savePrivileges(String ownerType, String owner, Set<String> privKeys) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断指定会员是否拥有指定权限
|
||||||
|
*
|
||||||
|
* @param memberId
|
||||||
|
* @param privKey
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean checkPriv(Long memberId, String privKey);
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package com.chestnut.member.core.impl;
|
||||||
|
|
||||||
|
import com.chestnut.member.core.MemberPrivType;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 会员菜单权限
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
public class MemberMenuPrivType implements MemberPrivType {
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checkPriv(Long memberId, String privKey) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package com.chestnut.member.privilege;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MemberPrivService
|
||||||
|
*
|
||||||
|
* @author 兮玥
|
||||||
|
* @email 190785909@qq.com
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class MemberPrivService {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存会员权限信息+
|
||||||
|
*
|
||||||
|
* @param ownerType
|
||||||
|
* @param owner
|
||||||
|
*/
|
||||||
|
public void savePrivilege(String ownerType, String owner, String privType, Set<String> privKeys) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,10 +17,11 @@ export function getContentTypes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 查询栏目树结构
|
// 查询栏目树结构
|
||||||
export function getCatalogTreeData() {
|
export function getCatalogTreeData(params) {
|
||||||
return request({
|
return request({
|
||||||
url: '/cms/catalog/treeData',
|
url: '/cms/catalog/treeData',
|
||||||
method: 'get'
|
method: 'get',
|
||||||
|
params: params
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
8
chestnut-ui/src/api/contentcore/dashboard.js
Normal file
8
chestnut-ui/src/api/contentcore/dashboard.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
export function getCmsConfiguration() {
|
||||||
|
return request({
|
||||||
|
url: '/cms/dashboard/config',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -781,6 +781,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
CMS: {
|
CMS: {
|
||||||
|
Dashboard: {
|
||||||
|
PublishStrategy: "Publish Strategy",
|
||||||
|
ResourceRoot: "Resource Root"
|
||||||
|
},
|
||||||
ContentCore: {
|
ContentCore: {
|
||||||
ContentType: "Content Type",
|
ContentType: "Content Type",
|
||||||
CatalogType: "Catalog Type",
|
CatalogType: "Catalog Type",
|
||||||
@ -1202,6 +1206,43 @@ export default {
|
|||||||
ScreenshotDialog: "Video Screenshot",
|
ScreenshotDialog: "Video Screenshot",
|
||||||
SetLogo: "Set Album Cover"
|
SetLogo: "Set Album Cover"
|
||||||
},
|
},
|
||||||
|
Book: {
|
||||||
|
Basic: "Book Info",
|
||||||
|
PublicationDate: "Pub. Date",
|
||||||
|
Publisher: "Publisher",
|
||||||
|
NumberOfPages: "Num. of pages",
|
||||||
|
NumberOfWords: "Num. of words",
|
||||||
|
Producer: "Producer",
|
||||||
|
OriginalTitle: "Original title",
|
||||||
|
Translators: "Translators",
|
||||||
|
Price: "Price",
|
||||||
|
CurrencyFen: "Fen",
|
||||||
|
Completed: "Completed",
|
||||||
|
Intro: "Introduce",
|
||||||
|
ChapterList: "Chapters",
|
||||||
|
InputChapterTitle: "Chapter title",
|
||||||
|
SortAsc: "ASC",
|
||||||
|
SortDesc: "DESC",
|
||||||
|
PublishDate: "Publish date",
|
||||||
|
PublishImmediately: "Publish Immediately",
|
||||||
|
ChapterTitle: "Chapter title",
|
||||||
|
Publish: "Publish",
|
||||||
|
ToPublish: "Scheduled",
|
||||||
|
Offline: "Offline",
|
||||||
|
ToPublishDialogTitle: "Scheduled Publish",
|
||||||
|
PublishSuccess: "Publish Success",
|
||||||
|
OfflineSuccess: "Offline Success",
|
||||||
|
ToPublishSuccess: "Scheduled Publish Success",
|
||||||
|
CloseChapterEditorTip: "Chapter data not saved, are you sure to quit?",
|
||||||
|
Route: {
|
||||||
|
EditChapter: "Edit book chapter"
|
||||||
|
},
|
||||||
|
RuleTips: {
|
||||||
|
Title: "Title cannot be empty.",
|
||||||
|
Content: "Content cannot be empty.",
|
||||||
|
PublishDate: "Publish date cannot be empty."
|
||||||
|
}
|
||||||
|
},
|
||||||
PageWidget: {
|
PageWidget: {
|
||||||
Type: "Type",
|
Type: "Type",
|
||||||
Name: "Name",
|
Name: "Name",
|
||||||
|
|||||||
@ -730,7 +730,7 @@ export default {
|
|||||||
JVMVersion: "Java版本",
|
JVMVersion: "Java版本",
|
||||||
JVMStartTime: "启动时间",
|
JVMStartTime: "启动时间",
|
||||||
JVMRunTime: "运行时间",
|
JVMRunTime: "运行时间",
|
||||||
JVMHome: "安装了路径",
|
JVMHome: "安装路径",
|
||||||
ProjectDir: "项目路径",
|
ProjectDir: "项目路径",
|
||||||
JVMArgs: "运行参数",
|
JVMArgs: "运行参数",
|
||||||
Disk: "磁盘状态",
|
Disk: "磁盘状态",
|
||||||
@ -781,6 +781,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
CMS: {
|
CMS: {
|
||||||
|
Dashboard: {
|
||||||
|
PublishStrategy: "发布策略",
|
||||||
|
ResourceRoot: "资源目录"
|
||||||
|
},
|
||||||
ContentCore: {
|
ContentCore: {
|
||||||
ContentType: "内容类型",
|
ContentType: "内容类型",
|
||||||
CatalogType: "栏目类型",
|
CatalogType: "栏目类型",
|
||||||
@ -1202,6 +1206,43 @@ export default {
|
|||||||
ScreenshotDialog: "视频封面截图",
|
ScreenshotDialog: "视频封面截图",
|
||||||
SetLogo: "设为视频集封面"
|
SetLogo: "设为视频集封面"
|
||||||
},
|
},
|
||||||
|
Book: {
|
||||||
|
Basic: "图书信息",
|
||||||
|
PublicationDate: "出版时间",
|
||||||
|
Publisher: "出版社",
|
||||||
|
NumberOfPages: "页数",
|
||||||
|
NumberOfWords: "字数",
|
||||||
|
Producer: "出品方",
|
||||||
|
OriginalTitle: "原著名",
|
||||||
|
Translators: "译者",
|
||||||
|
Price: "价格",
|
||||||
|
CurrencyFen: "分",
|
||||||
|
Completed: "是否完结",
|
||||||
|
Intro: "介绍",
|
||||||
|
ChapterList: "章节列表",
|
||||||
|
InputChapterTitle: "输入章节标题",
|
||||||
|
SortAsc: "顺序",
|
||||||
|
SortDesc: "倒序",
|
||||||
|
PublishDate: "发布时间",
|
||||||
|
PublishImmediately: "立即发布",
|
||||||
|
ChapterTitle: "章节标题",
|
||||||
|
Publish: "发布",
|
||||||
|
ToPublish: "定时发布",
|
||||||
|
Offline: "下线",
|
||||||
|
ToPublishDialogTitle: "定时发布",
|
||||||
|
PublishSuccess: "发布成功",
|
||||||
|
OfflineSuccess: "下线成功",
|
||||||
|
ToPublishSuccess: "定时发布成功",
|
||||||
|
CloseChapterEditorTip: "章节数据未保存,确认关闭吗?",
|
||||||
|
Route: {
|
||||||
|
EditChapter: "编辑章节"
|
||||||
|
},
|
||||||
|
RuleTips: {
|
||||||
|
Title: "标题不能为空",
|
||||||
|
Content: "章节内容不能为空",
|
||||||
|
PublishDate: "发布时间不能为空"
|
||||||
|
}
|
||||||
|
},
|
||||||
PageWidget: {
|
PageWidget: {
|
||||||
Type: "类型",
|
Type: "类型",
|
||||||
Name: "名称",
|
Name: "名称",
|
||||||
|
|||||||
@ -730,7 +730,7 @@ export default {
|
|||||||
JVMVersion: "Java版本",
|
JVMVersion: "Java版本",
|
||||||
JVMStartTime: "啟動時間",
|
JVMStartTime: "啟動時間",
|
||||||
JVMRunTime: "運行時間",
|
JVMRunTime: "運行時間",
|
||||||
JVMHome: "安裝了路徑",
|
JVMHome: "安裝路徑",
|
||||||
ProjectDir: "項目路徑",
|
ProjectDir: "項目路徑",
|
||||||
JVMArgs: "運行參數",
|
JVMArgs: "運行參數",
|
||||||
Disk: "磁碟狀態",
|
Disk: "磁碟狀態",
|
||||||
@ -781,6 +781,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
CMS: {
|
CMS: {
|
||||||
|
Dashboard: {
|
||||||
|
PublishStrategy: "發佈策略",
|
||||||
|
ResourceRoot: "資源目錄"
|
||||||
|
},
|
||||||
ContentCore: {
|
ContentCore: {
|
||||||
ContentType: "內容類型",
|
ContentType: "內容類型",
|
||||||
CatalogType: "欄目類型",
|
CatalogType: "欄目類型",
|
||||||
@ -1202,6 +1206,43 @@ export default {
|
|||||||
ScreenshotDialog: "視頻封面截圖",
|
ScreenshotDialog: "視頻封面截圖",
|
||||||
SetLogo: "設為視頻集封面"
|
SetLogo: "設為視頻集封面"
|
||||||
},
|
},
|
||||||
|
Book: {
|
||||||
|
Basic: "圖書信息",
|
||||||
|
PublicationDate: "出版時間",
|
||||||
|
Publisher: "出版社",
|
||||||
|
NumberOfPages: "頁數",
|
||||||
|
NumberOfWords: "字數",
|
||||||
|
Producer: "出品方",
|
||||||
|
OriginalTitle: "原著名",
|
||||||
|
Translators: "譯者",
|
||||||
|
Price: "價格",
|
||||||
|
CurrencyFen: "分",
|
||||||
|
Completed: "是否完結",
|
||||||
|
Intro: "介紹",
|
||||||
|
ChapterList: "章節列表",
|
||||||
|
InputChapterTitle: "輸入章節標題",
|
||||||
|
SortAsc: "順序",
|
||||||
|
SortDesc: "倒序",
|
||||||
|
PublishDate: "發佈時間",
|
||||||
|
PublishImmediately: "立即發佈",
|
||||||
|
ChapterTitle: "章節標題",
|
||||||
|
Publish: "發佈",
|
||||||
|
ToPublish: "定時發佈",
|
||||||
|
Offline: "下線",
|
||||||
|
ToPublishDialogTitle: "定時發佈",
|
||||||
|
PublishSuccess: "發佈成功",
|
||||||
|
OfflineSuccess: "下線成功",
|
||||||
|
ToPublishSuccess: "定時發佈成功",
|
||||||
|
CloseChapterEditorTip: "章節數據未保存,確認關閉嗎?",
|
||||||
|
Route: {
|
||||||
|
EditChapter: "編輯章節"
|
||||||
|
},
|
||||||
|
RuleTips: {
|
||||||
|
Title: "標題不能為空",
|
||||||
|
Content: "章節內容不能為空",
|
||||||
|
PublishDate: "發佈時間不能為空"
|
||||||
|
}
|
||||||
|
},
|
||||||
PageWidget: {
|
PageWidget: {
|
||||||
Type: "類型",
|
Type: "類型",
|
||||||
Name: "名稱",
|
Name: "名稱",
|
||||||
|
|||||||
@ -18,7 +18,7 @@ const service = axios.create({
|
|||||||
// axios中请求配置有baseURL选项,表示请求URL公共部分
|
// axios中请求配置有baseURL选项,表示请求URL公共部分
|
||||||
baseURL: process.env.VUE_APP_BASE_API,
|
baseURL: process.env.VUE_APP_BASE_API,
|
||||||
// 超时
|
// 超时
|
||||||
timeout: 10000
|
timeout: 60000
|
||||||
})
|
})
|
||||||
|
|
||||||
// request拦截器
|
// request拦截器
|
||||||
|
|||||||
@ -76,6 +76,7 @@ export default {
|
|||||||
watch: {
|
watch: {
|
||||||
tags(newVal) {
|
tags(newVal) {
|
||||||
this.tagList = newVal;
|
this.tagList = newVal;
|
||||||
|
this.$emit("change", newVal);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
|
|||||||
@ -171,7 +171,7 @@
|
|||||||
:command="pp"
|
:command="pp"
|
||||||
:name="pp.pipeCode"
|
:name="pp.pipeCode"
|
||||||
:label="pp.pipeName">
|
:label="pp.pipeName">
|
||||||
<el-divider content-position="left">模板配置</el-divider>
|
<el-divider content-position="left">{{ $t('CMS.Catalog.TemplateConfig') }}</el-divider>
|
||||||
<el-form-item :label="$t('CMS.Catalog.IndexTemplate')" prop="indexTemplate">
|
<el-form-item :label="$t('CMS.Catalog.IndexTemplate')" prop="indexTemplate">
|
||||||
<el-input v-model="pp.props.indexTemplate">
|
<el-input v-model="pp.props.indexTemplate">
|
||||||
<el-button
|
<el-button
|
||||||
@ -232,7 +232,7 @@
|
|||||||
type="primary"
|
type="primary"
|
||||||
@click="handleApplyToChildren('contentExTemplate')">{{ $t('CMS.Catalog.ApplyToChildren') }}</el-button>
|
@click="handleApplyToChildren('contentExTemplate')">{{ $t('CMS.Catalog.ApplyToChildren') }}</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-divider content-position="left">其他配置</el-divider>
|
<el-divider content-position="left">{{ $t('CMS.Catalog.OtherConfig') }}</el-divider>
|
||||||
<el-form-item :label="$t('CMS.Site.UEditorCss')">
|
<el-form-item :label="$t('CMS.Site.UEditorCss')">
|
||||||
<el-input v-model="pp.props.ueditorCss">
|
<el-input v-model="pp.props.ueditorCss">
|
||||||
<el-button
|
<el-button
|
||||||
@ -297,6 +297,7 @@
|
|||||||
<cms-catalog-selector
|
<cms-catalog-selector
|
||||||
:open="openCatalogSelector"
|
:open="openCatalogSelector"
|
||||||
:showRootNode="showCatalogSelectorRootNode"
|
:showRootNode="showCatalogSelectorRootNode"
|
||||||
|
:disableLink="disableLinkCatalog"
|
||||||
@ok="handleCatalogSelectorOk"
|
@ok="handleCatalogSelectorOk"
|
||||||
@close="handleCatalogSelectorClose"></cms-catalog-selector>
|
@close="handleCatalogSelectorClose"></cms-catalog-selector>
|
||||||
<!-- 内容选择组件 -->
|
<!-- 内容选择组件 -->
|
||||||
@ -355,6 +356,7 @@ export default {
|
|||||||
openCatalogSelector: false,
|
openCatalogSelector: false,
|
||||||
catalogSelectorFor: undefined,
|
catalogSelectorFor: undefined,
|
||||||
showCatalogSelectorRootNode: false,
|
showCatalogSelectorRootNode: false,
|
||||||
|
disableLinkCatalog: false,
|
||||||
openContentSelector: false,
|
openContentSelector: false,
|
||||||
openTemplateSelector: false, // 是否显示模板选择弹窗
|
openTemplateSelector: false, // 是否显示模板选择弹窗
|
||||||
propKey: "", // 选择模板时记录变更的模板对应属性Key
|
propKey: "", // 选择模板时记录变更的模板对应属性Key
|
||||||
@ -502,6 +504,7 @@ export default {
|
|||||||
this.catalogSelectorFor = "MoveCatalog";
|
this.catalogSelectorFor = "MoveCatalog";
|
||||||
this.openCatalogSelector = true;
|
this.openCatalogSelector = true;
|
||||||
this.showCatalogSelectorRootNode = true;
|
this.showCatalogSelectorRootNode = true;
|
||||||
|
this.disableLinkCatalog = false;
|
||||||
},
|
},
|
||||||
handleCloseProgress() {
|
handleCloseProgress() {
|
||||||
if (this.progressType == 'Delete' || this.progressType == 'Move') {
|
if (this.progressType == 'Delete' || this.progressType == 'Move') {
|
||||||
@ -540,6 +543,7 @@ export default {
|
|||||||
} else if (type === 'catalog') {
|
} else if (type === 'catalog') {
|
||||||
this.openCatalogSelector = true;
|
this.openCatalogSelector = true;
|
||||||
this.showCatalogSelectorRootNode = false;
|
this.showCatalogSelectorRootNode = false;
|
||||||
|
this.disableLinkCatalog = true;
|
||||||
this.catalogSelectorFor = "";
|
this.catalogSelectorFor = "";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -37,7 +37,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
v-if="showRootNode"
|
v-if="showRootNode"
|
||||||
type="text"
|
type="text"
|
||||||
:class="'tree-root' + (rootSelected?' is-current':'')"
|
:class="'tree-root' + (rootSelected?' cc-current':'')"
|
||||||
icon="el-icon-s-home"
|
icon="el-icon-s-home"
|
||||||
@click="handleTreeRootClick">{{ siteName }}</el-button>
|
@click="handleTreeRootClick">{{ siteName }}</el-button>
|
||||||
<el-tree
|
<el-tree
|
||||||
@ -51,8 +51,10 @@
|
|||||||
node-key="id"
|
node-key="id"
|
||||||
ref="tree"
|
ref="tree"
|
||||||
default-expand-all
|
default-expand-all
|
||||||
highlight-current
|
|
||||||
@node-click="handleNodeClick">
|
@node-click="handleNodeClick">
|
||||||
|
<template slot-scope="{ node, data }">
|
||||||
|
<span :id="'tn-'+node.id" :class="node.disabled?'cc-disabled':''">{{ node.label }}</span>
|
||||||
|
</template>
|
||||||
</el-tree>
|
</el-tree>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
@ -96,6 +98,12 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
required: false
|
required: false
|
||||||
|
},
|
||||||
|
// 是否不允许选择链接栏目
|
||||||
|
disableLink: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
required: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -143,7 +151,7 @@ export default {
|
|||||||
this.selectedCatalogs = [];
|
this.selectedCatalogs = [];
|
||||||
this.rootSelected = false;
|
this.rootSelected = false;
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
getCatalogTreeData().then(response => {
|
getCatalogTreeData({disableLink: this.disableLink}).then(response => {
|
||||||
if (response.code == 200) {
|
if (response.code == 200) {
|
||||||
this.catalogOptions = response.data.rows;
|
this.catalogOptions = response.data.rows;
|
||||||
this.siteName = response.data.siteName;
|
this.siteName = response.data.siteName;
|
||||||
@ -155,10 +163,21 @@ export default {
|
|||||||
if (!value) return true;
|
if (!value) return true;
|
||||||
return data.label.indexOf(value) > -1;
|
return data.label.indexOf(value) > -1;
|
||||||
},
|
},
|
||||||
handleNodeClick (data) {
|
setNodeHighlight(node) {
|
||||||
|
document.querySelectorAll(".cc-current").forEach(item => item.classList.remove("cc-current"));
|
||||||
|
if (node) {
|
||||||
|
document.querySelector("#tn-"+node.id).classList.add("cc-current");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleNodeClick (data, node) {
|
||||||
if (!this.multiple) {
|
if (!this.multiple) {
|
||||||
this.selectedCatalogs = [{ id: data.id, name: data.label, props: data.props }];
|
if (!this.disableLink || !data.disabled) {
|
||||||
this.rootSelected = false;
|
this.setNodeHighlight(node)
|
||||||
|
this.selectedCatalogs = [{ id: data.id, name: data.label, props: data.props }];
|
||||||
|
this.rootSelected = false;
|
||||||
|
} else {
|
||||||
|
this.$refs.tree.setCurrentKey(null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleTreeRootClick(e) {
|
handleTreeRootClick(e) {
|
||||||
@ -179,6 +198,7 @@ export default {
|
|||||||
this.$modal.alertWarning(this.$t('CMS.Catalog.SelectCatalogFirst'));
|
this.$modal.alertWarning(this.$t('CMS.Catalog.SelectCatalogFirst'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.setNodeHighlight()
|
||||||
this.$emit("ok", this.selectedCatalogs, this.copyType);
|
this.$emit("ok", this.selectedCatalogs, this.copyType);
|
||||||
},
|
},
|
||||||
handleCancel () {
|
handleCancel () {
|
||||||
@ -215,10 +235,14 @@ export default {
|
|||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
.catalog-selector .tree-container .is-current {
|
|
||||||
background-color: #edf6ff;
|
|
||||||
}
|
|
||||||
.catalog-selector .tree-container .tree-root:hover {
|
.catalog-selector .tree-container .tree-root:hover {
|
||||||
background-color: #F5F7FA;
|
background-color: #F5F7FA;
|
||||||
}
|
}
|
||||||
|
.catalog-selector .tree-container .cc-current {
|
||||||
|
color: #409EFF;
|
||||||
|
}
|
||||||
|
.catalog-selector .tree-container .cc-disabled {
|
||||||
|
color: #C0C4CC;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -17,7 +17,7 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row class="art-editor-container" :gutter="10" v-loading="loading">
|
<el-row class="art-editor-container" :gutter="10" v-loading="loading">
|
||||||
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
<el-form ref="form" :model="form" :rules="rules" label-width="110px">
|
||||||
<el-col :span="16">
|
<el-col :span="16">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col class="pr10">
|
<el-col class="pr10">
|
||||||
@ -241,6 +241,7 @@
|
|||||||
<!-- 栏目选择组件 -->
|
<!-- 栏目选择组件 -->
|
||||||
<cms-catalog-selector
|
<cms-catalog-selector
|
||||||
:open="openCatalogSelector"
|
:open="openCatalogSelector"
|
||||||
|
:disableLink="disableLinkCatalog"
|
||||||
@ok="handleCatalogSelectorOk"
|
@ok="handleCatalogSelectorOk"
|
||||||
@close="handleCatalogSelectorClose"></cms-catalog-selector>
|
@close="handleCatalogSelectorClose"></cms-catalog-selector>
|
||||||
<!-- 内容选择组件 -->
|
<!-- 内容选择组件 -->
|
||||||
@ -323,6 +324,7 @@ export default {
|
|||||||
contentType: this.$route.query.type,
|
contentType: this.$route.query.type,
|
||||||
opType: !this.$route.query.id || this.$route.query.id == '0' ? 'ADD' : 'UPDATE',
|
opType: !this.$route.query.id || this.$route.query.id == '0' ? 'ADD' : 'UPDATE',
|
||||||
openCatalogSelector: false,
|
openCatalogSelector: false,
|
||||||
|
disableLinkCatalog: false,
|
||||||
catalogSelectorFor: undefined,
|
catalogSelectorFor: undefined,
|
||||||
openContentSelector: false,
|
openContentSelector: false,
|
||||||
// 表单参数
|
// 表单参数
|
||||||
@ -576,6 +578,7 @@ export default {
|
|||||||
},
|
},
|
||||||
handleCatalogChange() {
|
handleCatalogChange() {
|
||||||
this.openCatalogSelector = true;
|
this.openCatalogSelector = true;
|
||||||
|
this.disableLinkCatalog = false;
|
||||||
this.catalogSelectorFor = "change";
|
this.catalogSelectorFor = "change";
|
||||||
},
|
},
|
||||||
handleCatalogSelectorOk(catalogs) {
|
handleCatalogSelectorOk(catalogs) {
|
||||||
@ -616,6 +619,7 @@ export default {
|
|||||||
} else if (type === 'catalog') {
|
} else if (type === 'catalog') {
|
||||||
this.openCatalogSelector = true;
|
this.openCatalogSelector = true;
|
||||||
this.catalogSelectorFor = 'linkflag';
|
this.catalogSelectorFor = 'linkflag';
|
||||||
|
this.disableLinkCatalog = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleContentSelectorOk(contents) {
|
handleContentSelectorOk(contents) {
|
||||||
|
|||||||
@ -232,7 +232,7 @@
|
|||||||
<el-table-column
|
<el-table-column
|
||||||
:label="$t('Common.Operation')"
|
:label="$t('Common.Operation')"
|
||||||
align="center"
|
align="center"
|
||||||
width="220"
|
width="260"
|
||||||
class-name="small-padding fixed-width">
|
class-name="small-padding fixed-width">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span class="btn-cell-wrap">
|
<span class="btn-cell-wrap">
|
||||||
@ -541,7 +541,6 @@ export default {
|
|||||||
}
|
}
|
||||||
this.isCopy = true;
|
this.isCopy = true;
|
||||||
this.openCatalogSelector = true;
|
this.openCatalogSelector = true;
|
||||||
console.log(this.selectedRows.map(item => item.contentId))
|
|
||||||
},
|
},
|
||||||
doCopy(catalogs, copyType) {
|
doCopy(catalogs, copyType) {
|
||||||
const data = {
|
const data = {
|
||||||
|
|||||||
@ -57,7 +57,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$t('CMS.PageWidget.Path')" align="left" width="180" prop="path" :show-overflow-tooltip="true" />
|
<el-table-column :label="$t('CMS.PageWidget.Path')" align="left" width="180" prop="path" :show-overflow-tooltip="true" />
|
||||||
<el-table-column :label="$t('Common.Operation')" align="left" width="200" class-name="small-padding fixed-width">
|
<el-table-column :label="$t('Common.Operation')" align="right" width="250" class-name="small-padding fixed-width">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span class="btn-cell-wrap">
|
<span class="btn-cell-wrap">
|
||||||
<el-button
|
<el-button
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<div class="dashboard-container">
|
<div class="dashboard-container">
|
||||||
<el-card shadow="hover" class="mb10">
|
<el-card shadow="hover" class="mb10">
|
||||||
<div slot="header" class="clearfix">
|
<div slot="header" class="clearfix">
|
||||||
<span>系统信息</span>
|
<span>{{ $t("Monitor.Server.ApplicationInfo") }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="el-table el-table--enable-row-hover el-table--medium">
|
<div class="el-table el-table--enable-row-hover el-table--medium">
|
||||||
<table cellspacing="0" style="width: 100%;">
|
<table cellspacing="0" style="width: 100%;">
|
||||||
@ -19,6 +19,14 @@
|
|||||||
<td class="el-table__cell is-leaf"><div class="cell attrname">{{ $t('Monitor.Server.JVMRunTime') }}</div></td>
|
<td class="el-table__cell is-leaf"><div class="cell attrname">{{ $t('Monitor.Server.JVMRunTime') }}</div></td>
|
||||||
<td class="el-table__cell is-leaf"><div class="cell">{{ serverInfo.runTime }}</div></td>
|
<td class="el-table__cell is-leaf"><div class="cell">{{ serverInfo.runTime }}</div></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="el-table__cell is-leaf"><div class="cell attrname">{{ $t('CMS.Dashboard.PublishStrategy') }}</div></td>
|
||||||
|
<td class="el-table__cell is-leaf" colspan="3"><div class="cell">{{ config.publishStrategy }}</div></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="el-table__cell is-leaf"><div class="cell attrname">{{ $t('CMS.Dashboard.ResourceRoot') }}</div></td>
|
||||||
|
<td class="el-table__cell is-leaf" colspan="3"><div class="cell">{{ config.resourceRoot }}</div></td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@ -28,6 +36,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { getDashboardServerInfo } from "@/api/monitor/server";
|
import { getDashboardServerInfo } from "@/api/monitor/server";
|
||||||
|
import { getCmsConfiguration } from "@/api/contentcore/dashboard";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ServerInfoDashboard",
|
name: "ServerInfoDashboard",
|
||||||
@ -35,17 +44,24 @@ export default {
|
|||||||
return {
|
return {
|
||||||
serverInfo: {
|
serverInfo: {
|
||||||
app: {}
|
app: {}
|
||||||
}
|
},
|
||||||
|
config:{}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.loadServerInfo();
|
this.loadServerInfo();
|
||||||
|
this.loadCmsConfiguration();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loadServerInfo() {
|
loadServerInfo() {
|
||||||
getDashboardServerInfo().then(response => {
|
getDashboardServerInfo().then(response => {
|
||||||
this.serverInfo = response.data;
|
this.serverInfo = response.data;
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
loadCmsConfiguration() {
|
||||||
|
getCmsConfiguration().then(response => {
|
||||||
|
this.config = response.data;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user