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"
|
||||
done
|
||||
|
||||
# 启动容器
|
||||
docker-compose up -d
|
||||
# 启动容器,老版本命令是docker-compose up -d
|
||||
docker compose up -d
|
||||
@ -17,6 +17,11 @@ chestnut:
|
||||
captchaType: math
|
||||
member:
|
||||
uploadPath: 'E:/dev/workspace_chestnut/_xy_member/'
|
||||
cms:
|
||||
publish:
|
||||
pool:
|
||||
threadNamePrefix: "CMS-PUBLISH-"
|
||||
queueCapacity: 10000
|
||||
|
||||
# 开发环境配置
|
||||
server:
|
||||
@ -120,7 +125,7 @@ spring:
|
||||
master:
|
||||
type: ${spring.datasource.type}
|
||||
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
|
||||
password: hello1234
|
||||
# 从库
|
||||
|
||||
@ -20,7 +20,11 @@ chestnut:
|
||||
cms:
|
||||
resourceRoot: /home/app/wwwroot_release
|
||||
publish:
|
||||
consumerCount: 1
|
||||
pool:
|
||||
threadNamePrefix: "CMS-PUBLISH-"
|
||||
queueCapacity: 10000
|
||||
coreSize: 2
|
||||
maxSize: 4
|
||||
|
||||
# 开发环境配置
|
||||
server:
|
||||
|
||||
@ -15,27 +15,30 @@
|
||||
*/
|
||||
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.utils.SpringUtils;
|
||||
import com.chestnut.common.utils.StringUtils;
|
||||
import com.chestnut.common.utils.file.FileExUtils;
|
||||
import com.chestnut.contentcore.ContentCoreConsts;
|
||||
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 freemarker.cache.FileTemplateLoader;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
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配置
|
||||
@ -45,7 +48,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(CMSProperties.class)
|
||||
@EnableConfigurationProperties({ CMSProperties.class, CMSPublishProperties.class })
|
||||
public class CMSConfig implements WebMvcConfigurer {
|
||||
|
||||
public static String CachePrefix = "cms:";
|
||||
@ -117,4 +120,10 @@ public class CMSConfig implements WebMvcConfigurer {
|
||||
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
|
||||
@Setter
|
||||
@ConfigurationProperties(prefix = "chestnut.cms.publish")
|
||||
@ConfigurationProperties(prefix = CMSPublishProperties.PREFIX)
|
||||
public class CMSPublishProperties {
|
||||
|
||||
public static final String PREFIX = "chestnut.cms.publish";
|
||||
|
||||
/**
|
||||
* 启动时清理发布消息队列
|
||||
*/
|
||||
@ -41,6 +43,11 @@ public class CMSPublishProperties {
|
||||
*/
|
||||
private int consumerCount = 2;
|
||||
|
||||
/**
|
||||
* 发布策略
|
||||
*/
|
||||
private String strategy;
|
||||
|
||||
private final AsyncProperties.Pool pool = new AsyncProperties.Pool();
|
||||
|
||||
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)
|
||||
@GetMapping("/treeData")
|
||||
public R<?> treeData() {
|
||||
public R<?> treeData(@RequestParam(required = false, defaultValue = "false") Boolean disableLink) {
|
||||
CmsSite site = this.siteService.getCurrentSite(ServletUtils.getRequest());
|
||||
LoginUser loginUser = StpAdminUtil.getLoginUser();
|
||||
List<CmsCatalog> catalogs = this.catalogService.lambdaQuery().eq(CmsCatalog::getSiteId, site.getSiteId())
|
||||
.orderByAsc(CmsCatalog::getSortFlag).list().stream().filter(c -> loginUser
|
||||
.hasPermission(CatalogPrivItem.View.getPermissionKey(c.getCatalogId())))
|
||||
.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()));
|
||||
}
|
||||
|
||||
|
||||
@ -144,7 +144,9 @@ public class CoreController extends BaseRestController {
|
||||
// init templateType data to datamode
|
||||
ITemplateType templateType = this.templateService.getTemplateType(SiteTemplateType.TypeId);
|
||||
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());
|
||||
// staticize
|
||||
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.security.anno.Priv;
|
||||
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 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.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@ -40,15 +38,14 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RequestMapping("/cms/publish/")
|
||||
public class PublishLogController extends BaseRestController {
|
||||
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
private final IPublishStrategy publishStrategy;
|
||||
|
||||
/**
|
||||
* 发布队列任务数量
|
||||
*/
|
||||
@GetMapping("/taskCount")
|
||||
public R<?> getPublishTaskCount() {
|
||||
StreamInfo.XInfoStream info = redisTemplate.opsForStream().info(CMSPublishConfig.PublishStreamName);
|
||||
return R.ok(info.streamLength());
|
||||
return R.ok(publishStrategy.getTaskCount());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,11 +53,7 @@ public class PublishLogController extends BaseRestController {
|
||||
*/
|
||||
@DeleteMapping("/clear")
|
||||
public R<?> clearPublishTask() {
|
||||
redisTemplate.delete(CMSPublishConfig.PublishStreamName);
|
||||
try {
|
||||
redisTemplate.opsForStream().createGroup(CMSPublishConfig.PublishStreamName, CMSPublishConfig.PublishConsumerGroup);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
publishStrategy.cleanTasks();
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,8 +178,6 @@ public class TemplateController extends BaseRestController {
|
||||
fileName = FileExUtils.normalizePath(fileName);
|
||||
String[] split = fileName.substring(0, fileName.indexOf(suffix)).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)) {
|
||||
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
|
||||
*/
|
||||
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.CmsSite;
|
||||
import com.chestnut.contentcore.fixed.dict.ContentStatus;
|
||||
import com.chestnut.contentcore.publish.CatalogPublishTask;
|
||||
import com.chestnut.contentcore.publish.ContentPublishTask;
|
||||
import com.chestnut.contentcore.publish.SitePublishTask;
|
||||
import com.chestnut.contentcore.publish.staticize.CatalogStaticizeType;
|
||||
import com.chestnut.contentcore.publish.staticize.ContentStaticizeType;
|
||||
import com.chestnut.contentcore.publish.staticize.SiteStaticizeType;
|
||||
import com.chestnut.contentcore.publish.IPublishStrategy;
|
||||
import com.chestnut.contentcore.service.ICatalogService;
|
||||
import com.chestnut.contentcore.service.IContentService;
|
||||
import com.chestnut.contentcore.service.ISiteService;
|
||||
@ -56,11 +57,7 @@ public class SitePublishJobHandler extends IJobHandler implements IScheduledHand
|
||||
|
||||
private final IContentService contentService;
|
||||
|
||||
private final SitePublishTask sitePublisher;
|
||||
|
||||
private final CatalogPublishTask catalogPublisher;
|
||||
|
||||
private final ContentPublishTask contentPublisher;
|
||||
private final IPublishStrategy publishStrategy;
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
@ -92,16 +89,16 @@ public class SitePublishJobHandler extends IJobHandler implements IScheduledHand
|
||||
for (int i = 0; i * pageSize < total; i++) {
|
||||
Page<CmsContent> page = contentService.page(new Page<>(i, pageSize, false), q);
|
||||
for (CmsContent xContent : page.getRecords()) {
|
||||
contentPublisher.publish(xContent);
|
||||
publishStrategy.publish(ContentStaticizeType.TYPE, xContent.getContentId().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
// 发布栏目
|
||||
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);
|
||||
}
|
||||
|
||||
@ -64,7 +64,6 @@ public class ImageWatermarkArgsProperty implements IProperty {
|
||||
public ImageWatermarkArgs getPropValue(Map<String, String> configProps) {
|
||||
String v = MapUtils.getString(configProps, ID);
|
||||
if (StringUtils.isNotEmpty(v)) {
|
||||
System.out.println(v);
|
||||
return JacksonUtils.from(v, ImageWatermarkArgs.class);
|
||||
}
|
||||
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.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface ICatalogService extends IService<CmsCatalog> {
|
||||
|
||||
@ -52,7 +54,7 @@ public interface ICatalogService extends IService<CmsCatalog> {
|
||||
* @param catalogs
|
||||
* @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.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@ -129,7 +130,7 @@ public class CatalogServiceImpl extends ServiceImpl<CmsCatalogMapper, CmsCatalog
|
||||
}
|
||||
|
||||
@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)) {
|
||||
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.getDescription() == null ? "" : c.getDescription());
|
||||
treeNode.setProps(props);
|
||||
consumer.accept(c, treeNode);
|
||||
return treeNode;
|
||||
}).toList();
|
||||
return TreeNode.build(list);
|
||||
|
||||
@ -91,7 +91,7 @@ public class DynamicPageService {
|
||||
// init template datamode
|
||||
TemplateUtils.initGlobalVariables(site, templateContext);
|
||||
// init templateType data to datamode
|
||||
templateContext.getVariables().put("Request", ServletUtils.getParameters());
|
||||
templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, ServletUtils.getParameters());
|
||||
dpt.initTemplateData(parameters, templateContext);
|
||||
// staticize
|
||||
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.listener.event.AfterContentPublishEvent;
|
||||
import com.chestnut.contentcore.properties.MaxPageOnContentPublishProperty;
|
||||
import com.chestnut.contentcore.publish.CatalogPublishTask;
|
||||
import com.chestnut.contentcore.publish.ContentPublishTask;
|
||||
import com.chestnut.contentcore.publish.SitePublishTask;
|
||||
import com.chestnut.contentcore.publish.staticize.CatalogStaticizeType;
|
||||
import com.chestnut.contentcore.publish.staticize.ContentStaticizeType;
|
||||
import com.chestnut.contentcore.publish.staticize.SiteStaticizeType;
|
||||
import com.chestnut.contentcore.publish.IPublishStrategy;
|
||||
import com.chestnut.contentcore.service.*;
|
||||
import com.chestnut.contentcore.template.ITemplateType;
|
||||
import com.chestnut.contentcore.template.impl.CatalogTemplateType;
|
||||
@ -80,11 +81,7 @@ public class PublishServiceImpl implements IPublishService, ApplicationContextAw
|
||||
|
||||
private final AsyncTaskManager asyncTaskManager;
|
||||
|
||||
private final SitePublishTask sitePublishTask;
|
||||
|
||||
private final CatalogPublishTask catalogPublishTask;
|
||||
|
||||
private final ContentPublishTask contentPublishTask;
|
||||
private final IPublishStrategy publishStrategy;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@ -191,7 +188,7 @@ public class PublishServiceImpl implements IPublishService, ApplicationContextAw
|
||||
}
|
||||
|
||||
private void asyncPublishSite(CmsSite site) {
|
||||
sitePublishTask.publish(site);
|
||||
publishStrategy.publish(SiteStaticizeType.TYPE, site.getSiteId().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -361,7 +358,7 @@ public class PublishServiceImpl implements IPublishService, ApplicationContextAw
|
||||
}
|
||||
|
||||
public void asyncPublishCatalog(final CmsCatalog catalog) {
|
||||
catalogPublishTask.publish(catalog);
|
||||
publishStrategy.publish(CatalogStaticizeType.TYPE, catalog.getCatalogId().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -572,14 +569,14 @@ public class PublishServiceImpl implements IPublishService, ApplicationContextAw
|
||||
if (publishPipeCodes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
contentPublishTask.publish(content.getContentEntity());
|
||||
publishStrategy.publish(ContentStaticizeType.TYPE, content.getContentEntity().getContentId().toString());
|
||||
// 关联内容静态化,映射的引用内容
|
||||
LambdaQueryWrapper<CmsContent> q = new LambdaQueryWrapper<CmsContent>()
|
||||
.eq(CmsContent::getCopyId, content.getContentEntity().getContentId())
|
||||
.eq(CmsContent::getCopyType, ContentCopyType.Mapping);
|
||||
List<CmsContent> mappingContents = contentService.list(q);
|
||||
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))
|
||||
.findFirst();
|
||||
opt.ifPresentOrElse(t -> {
|
||||
System.out.println("scan template: " + file.getName() + "|" + file.lastModified() + " = " + t.getModifyTime());
|
||||
if (t.getModifyTime() != file.lastModified()) {
|
||||
try {
|
||||
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.TemplateUtils;
|
||||
import freemarker.core.Environment;
|
||||
import freemarker.template.Template;
|
||||
import freemarker.template.TemplateException;
|
||||
import freemarker.template.TemplateModel;
|
||||
import freemarker.template.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -47,6 +46,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
public class CmsIncludeTag extends AbstractTag {
|
||||
@ -128,7 +128,11 @@ public class CmsIncludeTag extends AbstractTag {
|
||||
if (context.isPreview()) {
|
||||
Template includeTemplate = env.getTemplateForInclusion(includeTemplateKey,
|
||||
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);
|
||||
} else if (virtual) {
|
||||
// 动态模板
|
||||
@ -159,6 +163,23 @@ public class CmsIncludeTag extends AbstractTag {
|
||||
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);
|
||||
Template includeTemplate = env.getTemplateForInclusion(includeTemplateName,
|
||||
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);
|
||||
return writer.getBuffer().toString();
|
||||
} finally {
|
||||
|
||||
@ -38,6 +38,16 @@ import java.util.Map;
|
||||
@RequiredArgsConstructor
|
||||
public class TemplateUtils {
|
||||
|
||||
/**
|
||||
* 模板变量:请求参数
|
||||
*/
|
||||
public final static String TemplateVariable_Request = "Request";
|
||||
|
||||
/**
|
||||
* 模板变量:<@cms_include>标签file属性请求参数
|
||||
*/
|
||||
public final static String TemplateVariable_IncludeRequest = "IncludeRequest";
|
||||
|
||||
/**
|
||||
* 模板变量:预览模式登录用户token键名
|
||||
*/
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
package com.chestnut.cms.dynamic.core.impl;
|
||||
|
||||
import com.chestnut.cms.dynamic.core.IDynamicPageInitData;
|
||||
import com.chestnut.cms.member.CmsMemberConstants;
|
||||
import com.chestnut.common.security.domain.LoginUser;
|
||||
import com.chestnut.common.staticize.core.TemplateContext;
|
||||
import com.chestnut.member.security.StpMemberUtil;
|
||||
@ -49,8 +50,9 @@ public class MemberDynamicPageInitData implements IDynamicPageInitData {
|
||||
public void initTemplateData(TemplateContext context, Map<String, String> parameters) {
|
||||
if (StpMemberUtil.isLogin()) {
|
||||
LoginUser loginUser = StpMemberUtil.getLoginUser();
|
||||
context.getVariables().put("Member", loginUser.getUser());
|
||||
context.getVariables().put("MemberResourcePrefix", MemberUtils.getMemberResourcePrefix(context.isPreview()));
|
||||
context.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER, loginUser.getUser());
|
||||
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
|
||||
TemplateUtils.initGlobalVariables(site, templateContext);
|
||||
// init templateType data to datamode
|
||||
templateContext.getVariables().put("Request", parameters);
|
||||
templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, parameters);
|
||||
// 动态页面自定义数据
|
||||
if (Objects.nonNull(dynamicPage.getInitDataTypes())) {
|
||||
dynamicPage.getInitDataTypes().forEach(initDataType -> {
|
||||
|
||||
@ -26,4 +26,14 @@ public interface CmsMemberConstants {
|
||||
String MEMBER_FAVORITES_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;
|
||||
|
||||
import com.chestnut.cms.member.CmsMemberConstants;
|
||||
import com.chestnut.cms.member.publishpipe.PublishPipeProp_MemberBindEmailTemplate;
|
||||
import com.chestnut.common.staticize.core.TemplateContext;
|
||||
import com.chestnut.common.utils.ServletUtils;
|
||||
import com.chestnut.contentcore.core.IDynamicPageType;
|
||||
import com.chestnut.contentcore.util.TemplateUtils;
|
||||
import com.chestnut.member.domain.Member;
|
||||
import com.chestnut.member.domain.vo.MemberCache;
|
||||
import com.chestnut.member.service.IMemberService;
|
||||
@ -95,8 +97,9 @@ public class AccountBindEmailDynamicPageType implements IDynamicPageType {
|
||||
public void initTemplateData(Map<String, String> parameters, TemplateContext templateContext) {
|
||||
Long memberId = MapUtils.getLong(parameters, "memberId");
|
||||
Member member = this.memberService.getById(memberId);
|
||||
templateContext.getVariables().put("Member", member);
|
||||
templateContext.getVariables().put("MemberResourcePrefix", MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
||||
templateContext.getVariables().put("Request", parameters);
|
||||
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER, member);
|
||||
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER_RESOURCE_PREFIX,
|
||||
MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
||||
templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,9 +15,11 @@
|
||||
*/
|
||||
package com.chestnut.cms.member.impl;
|
||||
|
||||
import com.chestnut.cms.member.CmsMemberConstants;
|
||||
import com.chestnut.cms.member.publishpipe.PublishPipeProp_AccountCentreTemplate;
|
||||
import com.chestnut.common.staticize.core.TemplateContext;
|
||||
import com.chestnut.contentcore.core.IDynamicPageType;
|
||||
import com.chestnut.contentcore.util.TemplateUtils;
|
||||
import com.chestnut.member.domain.vo.MemberCache;
|
||||
import com.chestnut.member.service.IMemberStatDataService;
|
||||
import com.chestnut.member.util.MemberUtils;
|
||||
@ -92,9 +94,10 @@ public class AccountCentreDynamicPageType implements IDynamicPageType {
|
||||
Long siteId = MapUtils.getLong(parameters, "sid");
|
||||
Long memberId = MapUtils.getLong(parameters, "memberId");
|
||||
MemberCache member = this.memberStatDataService.getMemberCache(memberId);
|
||||
templateContext.getVariables().put("Member", member);
|
||||
templateContext.getVariables().put("MemberResourcePrefix", MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
||||
templateContext.getVariables().put("Request", parameters);
|
||||
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER, member);
|
||||
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER_RESOURCE_PREFIX,
|
||||
MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
||||
templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, parameters);
|
||||
templateContext.setPageIndex(MapUtils.getIntValue(parameters, "page", 1));
|
||||
|
||||
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.service.IArticleService;
|
||||
import com.chestnut.cms.member.CmsMemberConstants;
|
||||
import com.chestnut.cms.member.domain.vo.ContributeArticleVO;
|
||||
import com.chestnut.cms.member.publishpipe.PublishPipeProp_MemberContributeTemplate;
|
||||
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.service.IContentService;
|
||||
import com.chestnut.contentcore.util.InternalUrlUtils;
|
||||
import com.chestnut.contentcore.util.TemplateUtils;
|
||||
import com.chestnut.member.domain.Member;
|
||||
import com.chestnut.member.domain.vo.MemberCache;
|
||||
import com.chestnut.member.service.IMemberService;
|
||||
@ -108,9 +110,10 @@ public class AccountContributeDynamicPageType implements IDynamicPageType {
|
||||
public void initTemplateData(Map<String, String> parameters, TemplateContext templateContext) {
|
||||
Long memberId = MapUtils.getLong(parameters, "memberId");
|
||||
Member member = this.memberService.getById(memberId);
|
||||
templateContext.getVariables().put("Member", member);
|
||||
templateContext.getVariables().put("MemberResourcePrefix", MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
||||
templateContext.getVariables().put("Request", parameters);
|
||||
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER, member);
|
||||
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER_RESOURCE_PREFIX,
|
||||
MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
||||
templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, parameters);
|
||||
|
||||
Long contentId = MapUtils.getLong(parameters, "cid", 0L);
|
||||
if (IdUtils.validate(contentId)) {
|
||||
|
||||
@ -15,10 +15,12 @@
|
||||
*/
|
||||
package com.chestnut.cms.member.impl;
|
||||
|
||||
import com.chestnut.cms.member.CmsMemberConstants;
|
||||
import com.chestnut.cms.member.publishpipe.PublishPipeProp_MemberPasswordTemplate;
|
||||
import com.chestnut.common.staticize.core.TemplateContext;
|
||||
import com.chestnut.common.utils.ServletUtils;
|
||||
import com.chestnut.contentcore.core.IDynamicPageType;
|
||||
import com.chestnut.contentcore.util.TemplateUtils;
|
||||
import com.chestnut.member.domain.Member;
|
||||
import com.chestnut.member.domain.vo.MemberCache;
|
||||
import com.chestnut.member.service.IMemberService;
|
||||
@ -95,8 +97,9 @@ public class AccountPasswordDynamicPageType implements IDynamicPageType {
|
||||
public void initTemplateData(Map<String, String> parameters, TemplateContext templateContext) {
|
||||
Long memberId = MapUtils.getLong(parameters, "memberId");
|
||||
Member member = this.memberService.getById(memberId);
|
||||
templateContext.getVariables().put("Member", member);
|
||||
templateContext.getVariables().put("MemberResourcePrefix", MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
||||
templateContext.getVariables().put("Request", ServletUtils.getParameters());
|
||||
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER, member);
|
||||
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER_RESOURCE_PREFIX,
|
||||
MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
||||
templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, ServletUtils.getParameters());
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,10 +15,12 @@
|
||||
*/
|
||||
package com.chestnut.cms.member.impl;
|
||||
|
||||
import com.chestnut.cms.member.CmsMemberConstants;
|
||||
import com.chestnut.cms.member.publishpipe.PublishPipeProp_MemberSettingTemplate;
|
||||
import com.chestnut.common.staticize.core.TemplateContext;
|
||||
import com.chestnut.common.utils.ServletUtils;
|
||||
import com.chestnut.contentcore.core.IDynamicPageType;
|
||||
import com.chestnut.contentcore.util.TemplateUtils;
|
||||
import com.chestnut.member.domain.Member;
|
||||
import com.chestnut.member.domain.vo.MemberCache;
|
||||
import com.chestnut.member.service.IMemberService;
|
||||
@ -95,8 +97,9 @@ public class AccountSettingDynamicPageType implements IDynamicPageType {
|
||||
public void initTemplateData(Map<String, String> parameters, TemplateContext templateContext) {
|
||||
Long memberId = MapUtils.getLong(parameters, "memberId");
|
||||
Member member = this.memberService.getById(memberId);
|
||||
templateContext.getVariables().put("Member", member);
|
||||
templateContext.getVariables().put("MemberResourcePrefix", MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
||||
templateContext.getVariables().put("Request", parameters);
|
||||
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER, member);
|
||||
templateContext.getVariables().put(CmsMemberConstants.TEMPLATE_VARIABLE_MEMBER_RESOURCE_PREFIX,
|
||||
MemberUtils.getMemberResourcePrefix(templateContext.isPreview()));
|
||||
templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ public class SearchDynamicPageType implements IDynamicPageType {
|
||||
|
||||
@Override
|
||||
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");
|
||||
if (templateContext.isPreview()) {
|
||||
link += "&sid=" + parameters.get("sid") + "&pp=" + templateContext.getPublishPipeCode() + "&preview=true";
|
||||
|
||||
@ -15,6 +15,11 @@
|
||||
*/
|
||||
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.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@ -22,13 +27,14 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
/**
|
||||
* 树结构节点实体类
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class TreeNode<T> implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 节点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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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() {
|
||||
if (props == null) {
|
||||
props = new HashMap<>();
|
||||
}
|
||||
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.util.LinkedHashMap;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Slf4j
|
||||
public class ChineseSpelling {
|
||||
|
||||
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编码
|
||||
*
|
||||
|
||||
@ -20,7 +20,6 @@ import org.springframework.util.AntPathMatcher;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -743,9 +742,6 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, String> getPathParameterMap(String path) {
|
||||
if (Objects.isNull(path) || !path.contains("?")) {
|
||||
return Map.of();
|
||||
}
|
||||
String str = substringAfter(path, "?");
|
||||
return splitToMap(str, "&", "=");
|
||||
}
|
||||
|
||||
@ -199,12 +199,12 @@ public class FreeMarkerUtils {
|
||||
|
||||
private static TemplateModel evalTemplateModel(Environment env, String[] names) throws TemplateModelException {
|
||||
TemplateModel model = env.getVariable(names[0]);
|
||||
if (model == null || !(model instanceof TemplateHashModel)) {
|
||||
if (!(model instanceof TemplateHashModel)) {
|
||||
throw new TemplateModelException();
|
||||
}
|
||||
for (int i = 1; i < names.length - 1; i++) {
|
||||
model = ((TemplateHashModel) model).get(names[i]);
|
||||
if (model == null || !(model instanceof TemplateHashModel)) {
|
||||
if (!(model instanceof TemplateHashModel)) {
|
||||
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({
|
||||
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: {
|
||||
Dashboard: {
|
||||
PublishStrategy: "Publish Strategy",
|
||||
ResourceRoot: "Resource Root"
|
||||
},
|
||||
ContentCore: {
|
||||
ContentType: "Content Type",
|
||||
CatalogType: "Catalog Type",
|
||||
@ -1202,6 +1206,43 @@ export default {
|
||||
ScreenshotDialog: "Video Screenshot",
|
||||
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: {
|
||||
Type: "Type",
|
||||
Name: "Name",
|
||||
|
||||
@ -730,7 +730,7 @@ export default {
|
||||
JVMVersion: "Java版本",
|
||||
JVMStartTime: "启动时间",
|
||||
JVMRunTime: "运行时间",
|
||||
JVMHome: "安装了路径",
|
||||
JVMHome: "安装路径",
|
||||
ProjectDir: "项目路径",
|
||||
JVMArgs: "运行参数",
|
||||
Disk: "磁盘状态",
|
||||
@ -781,6 +781,10 @@ export default {
|
||||
}
|
||||
},
|
||||
CMS: {
|
||||
Dashboard: {
|
||||
PublishStrategy: "发布策略",
|
||||
ResourceRoot: "资源目录"
|
||||
},
|
||||
ContentCore: {
|
||||
ContentType: "内容类型",
|
||||
CatalogType: "栏目类型",
|
||||
@ -1202,6 +1206,43 @@ export default {
|
||||
ScreenshotDialog: "视频封面截图",
|
||||
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: {
|
||||
Type: "类型",
|
||||
Name: "名称",
|
||||
|
||||
@ -730,7 +730,7 @@ export default {
|
||||
JVMVersion: "Java版本",
|
||||
JVMStartTime: "啟動時間",
|
||||
JVMRunTime: "運行時間",
|
||||
JVMHome: "安裝了路徑",
|
||||
JVMHome: "安裝路徑",
|
||||
ProjectDir: "項目路徑",
|
||||
JVMArgs: "運行參數",
|
||||
Disk: "磁碟狀態",
|
||||
@ -781,6 +781,10 @@ export default {
|
||||
}
|
||||
},
|
||||
CMS: {
|
||||
Dashboard: {
|
||||
PublishStrategy: "發佈策略",
|
||||
ResourceRoot: "資源目錄"
|
||||
},
|
||||
ContentCore: {
|
||||
ContentType: "內容類型",
|
||||
CatalogType: "欄目類型",
|
||||
@ -1202,6 +1206,43 @@ export default {
|
||||
ScreenshotDialog: "視頻封面截圖",
|
||||
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: {
|
||||
Type: "類型",
|
||||
Name: "名稱",
|
||||
|
||||
@ -18,7 +18,7 @@ const service = axios.create({
|
||||
// axios中请求配置有baseURL选项,表示请求URL公共部分
|
||||
baseURL: process.env.VUE_APP_BASE_API,
|
||||
// 超时
|
||||
timeout: 10000
|
||||
timeout: 60000
|
||||
})
|
||||
|
||||
// request拦截器
|
||||
|
||||
@ -76,6 +76,7 @@ export default {
|
||||
watch: {
|
||||
tags(newVal) {
|
||||
this.tagList = newVal;
|
||||
this.$emit("change", newVal);
|
||||
}
|
||||
},
|
||||
data () {
|
||||
|
||||
@ -171,7 +171,7 @@
|
||||
:command="pp"
|
||||
:name="pp.pipeCode"
|
||||
: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-input v-model="pp.props.indexTemplate">
|
||||
<el-button
|
||||
@ -232,7 +232,7 @@
|
||||
type="primary"
|
||||
@click="handleApplyToChildren('contentExTemplate')">{{ $t('CMS.Catalog.ApplyToChildren') }}</el-button>
|
||||
</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-input v-model="pp.props.ueditorCss">
|
||||
<el-button
|
||||
@ -297,6 +297,7 @@
|
||||
<cms-catalog-selector
|
||||
:open="openCatalogSelector"
|
||||
:showRootNode="showCatalogSelectorRootNode"
|
||||
:disableLink="disableLinkCatalog"
|
||||
@ok="handleCatalogSelectorOk"
|
||||
@close="handleCatalogSelectorClose"></cms-catalog-selector>
|
||||
<!-- 内容选择组件 -->
|
||||
@ -355,6 +356,7 @@ export default {
|
||||
openCatalogSelector: false,
|
||||
catalogSelectorFor: undefined,
|
||||
showCatalogSelectorRootNode: false,
|
||||
disableLinkCatalog: false,
|
||||
openContentSelector: false,
|
||||
openTemplateSelector: false, // 是否显示模板选择弹窗
|
||||
propKey: "", // 选择模板时记录变更的模板对应属性Key
|
||||
@ -502,6 +504,7 @@ export default {
|
||||
this.catalogSelectorFor = "MoveCatalog";
|
||||
this.openCatalogSelector = true;
|
||||
this.showCatalogSelectorRootNode = true;
|
||||
this.disableLinkCatalog = false;
|
||||
},
|
||||
handleCloseProgress() {
|
||||
if (this.progressType == 'Delete' || this.progressType == 'Move') {
|
||||
@ -540,6 +543,7 @@ export default {
|
||||
} else if (type === 'catalog') {
|
||||
this.openCatalogSelector = true;
|
||||
this.showCatalogSelectorRootNode = false;
|
||||
this.disableLinkCatalog = true;
|
||||
this.catalogSelectorFor = "";
|
||||
}
|
||||
},
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
<el-button
|
||||
v-if="showRootNode"
|
||||
type="text"
|
||||
:class="'tree-root' + (rootSelected?' is-current':'')"
|
||||
:class="'tree-root' + (rootSelected?' cc-current':'')"
|
||||
icon="el-icon-s-home"
|
||||
@click="handleTreeRootClick">{{ siteName }}</el-button>
|
||||
<el-tree
|
||||
@ -51,8 +51,10 @@
|
||||
node-key="id"
|
||||
ref="tree"
|
||||
default-expand-all
|
||||
highlight-current
|
||||
@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-scrollbar>
|
||||
</div>
|
||||
@ -96,6 +98,12 @@ export default {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false
|
||||
},
|
||||
// 是否不允许选择链接栏目
|
||||
disableLink: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -143,7 +151,7 @@ export default {
|
||||
this.selectedCatalogs = [];
|
||||
this.rootSelected = false;
|
||||
this.loading = true;
|
||||
getCatalogTreeData().then(response => {
|
||||
getCatalogTreeData({disableLink: this.disableLink}).then(response => {
|
||||
if (response.code == 200) {
|
||||
this.catalogOptions = response.data.rows;
|
||||
this.siteName = response.data.siteName;
|
||||
@ -155,10 +163,21 @@ export default {
|
||||
if (!value) return true;
|
||||
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.disableLink || !data.disabled) {
|
||||
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) {
|
||||
@ -179,6 +198,7 @@ export default {
|
||||
this.$modal.alertWarning(this.$t('CMS.Catalog.SelectCatalogFirst'));
|
||||
return;
|
||||
}
|
||||
this.setNodeHighlight()
|
||||
this.$emit("ok", this.selectedCatalogs, this.copyType);
|
||||
},
|
||||
handleCancel () {
|
||||
@ -215,10 +235,14 @@ export default {
|
||||
border-radius: 0;
|
||||
padding: 5px;
|
||||
}
|
||||
.catalog-selector .tree-container .is-current {
|
||||
background-color: #edf6ff;
|
||||
}
|
||||
.catalog-selector .tree-container .tree-root:hover {
|
||||
background-color: #F5F7FA;
|
||||
}
|
||||
.catalog-selector .tree-container .cc-current {
|
||||
color: #409EFF;
|
||||
}
|
||||
.catalog-selector .tree-container .cc-disabled {
|
||||
color: #C0C4CC;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
@ -17,7 +17,7 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
<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-row>
|
||||
<el-col class="pr10">
|
||||
@ -241,6 +241,7 @@
|
||||
<!-- 栏目选择组件 -->
|
||||
<cms-catalog-selector
|
||||
:open="openCatalogSelector"
|
||||
:disableLink="disableLinkCatalog"
|
||||
@ok="handleCatalogSelectorOk"
|
||||
@close="handleCatalogSelectorClose"></cms-catalog-selector>
|
||||
<!-- 内容选择组件 -->
|
||||
@ -323,6 +324,7 @@ export default {
|
||||
contentType: this.$route.query.type,
|
||||
opType: !this.$route.query.id || this.$route.query.id == '0' ? 'ADD' : 'UPDATE',
|
||||
openCatalogSelector: false,
|
||||
disableLinkCatalog: false,
|
||||
catalogSelectorFor: undefined,
|
||||
openContentSelector: false,
|
||||
// 表单参数
|
||||
@ -576,6 +578,7 @@ export default {
|
||||
},
|
||||
handleCatalogChange() {
|
||||
this.openCatalogSelector = true;
|
||||
this.disableLinkCatalog = false;
|
||||
this.catalogSelectorFor = "change";
|
||||
},
|
||||
handleCatalogSelectorOk(catalogs) {
|
||||
@ -616,6 +619,7 @@ export default {
|
||||
} else if (type === 'catalog') {
|
||||
this.openCatalogSelector = true;
|
||||
this.catalogSelectorFor = 'linkflag';
|
||||
this.disableLinkCatalog = true;
|
||||
}
|
||||
},
|
||||
handleContentSelectorOk(contents) {
|
||||
|
||||
@ -232,7 +232,7 @@
|
||||
<el-table-column
|
||||
:label="$t('Common.Operation')"
|
||||
align="center"
|
||||
width="220"
|
||||
width="260"
|
||||
class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<span class="btn-cell-wrap">
|
||||
@ -541,7 +541,6 @@ export default {
|
||||
}
|
||||
this.isCopy = true;
|
||||
this.openCatalogSelector = true;
|
||||
console.log(this.selectedRows.map(item => item.contentId))
|
||||
},
|
||||
doCopy(catalogs, copyType) {
|
||||
const data = {
|
||||
|
||||
@ -57,7 +57,7 @@
|
||||
</template>
|
||||
</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('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">
|
||||
<span class="btn-cell-wrap">
|
||||
<el-button
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div class="dashboard-container">
|
||||
<el-card shadow="hover" class="mb10">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>系统信息</span>
|
||||
<span>{{ $t("Monitor.Server.ApplicationInfo") }}</span>
|
||||
</div>
|
||||
<div class="el-table el-table--enable-row-hover el-table--medium">
|
||||
<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">{{ serverInfo.runTime }}</div></td>
|
||||
</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>
|
||||
</table>
|
||||
</div>
|
||||
@ -28,6 +36,7 @@
|
||||
</template>
|
||||
<script>
|
||||
import { getDashboardServerInfo } from "@/api/monitor/server";
|
||||
import { getCmsConfiguration } from "@/api/contentcore/dashboard";
|
||||
|
||||
export default {
|
||||
name: "ServerInfoDashboard",
|
||||
@ -35,17 +44,24 @@ export default {
|
||||
return {
|
||||
serverInfo: {
|
||||
app: {}
|
||||
}
|
||||
},
|
||||
config:{}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.loadServerInfo();
|
||||
this.loadCmsConfiguration();
|
||||
},
|
||||
methods: {
|
||||
loadServerInfo() {
|
||||
getDashboardServerInfo().then(response => {
|
||||
this.serverInfo = response.data;
|
||||
})
|
||||
},
|
||||
loadCmsConfiguration() {
|
||||
getCmsConfiguration().then(response => {
|
||||
this.config = response.data;
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user