删除多余文件

This commit is contained in:
liweiyi 2024-08-31 18:31:01 +08:00
parent c772790270
commit 3e029c380e
29 changed files with 0 additions and 2253 deletions

View File

@ -1,107 +0,0 @@
/*
* 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.config;
import com.chestnut.contentcore.config.properties.CMSPublishProperties;
import com.chestnut.contentcore.publish.IPublishTask;
import com.chestnut.contentcore.publish.PublishTaskReceiver;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.stream.Consumer;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.connection.stream.ReadOffset;
import org.springframework.data.redis.connection.stream.StreamOffset;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.stream.StreamMessageListenerContainer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.time.Duration;
import java.util.Map;
/**
* 内容发布配置
*
* @author 兮玥
* @email 190785909@qq.com
*/
@Slf4j
@Configuration
@EnableConfigurationProperties(CMSPublishProperties.class)
@RequiredArgsConstructor
public class CMSPublishConfig {
private final CMSPublishProperties properties;
private final StringRedisTemplate redisTemplate;
private final Map<String, IPublishTask<?>> publishTaskMap;
public static final String PublishStreamName = "ChestnutCMSPublishStream";
public static final String PublishConsumerGroup = "ChestnutCMSPublishConsumerGroup";
@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(publishTaskMap, 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;
}
}

View File

@ -1,22 +0,0 @@
package com.chestnut.contentcore.listener.event;
import com.chestnut.contentcore.domain.CmsSite;
import jodd.io.ZipBuilder;
import lombok.Getter;
import org.springframework.context.ApplicationEvent;
@Getter
public class SiteThemeExportEvent extends ApplicationEvent {
private static final long serialVersionUID = 1L;
private CmsSite site;
private ZipBuilder zipBuilder;
public SiteThemeExportEvent(Object source, CmsSite site, ZipBuilder zipBuilder) {
super(source);
this.site = site;
this.zipBuilder = zipBuilder;
}
}

View File

@ -1,25 +0,0 @@
package com.chestnut.contentcore.listener.event;
import com.chestnut.common.security.domain.LoginUser;
import com.chestnut.contentcore.domain.CmsSite;
import lombok.Getter;
import org.springframework.context.ApplicationEvent;
@Getter
public class SiteThemeImportEvent extends ApplicationEvent {
private static final long serialVersionUID = 1L;
private CmsSite site;
private String destDir;
private LoginUser operator;
public SiteThemeImportEvent(Object source, CmsSite site, String destDir, LoginUser operator) {
super(source);
this.site = site;
this.destDir = destDir;
this.operator = operator;
}
}

View File

@ -1,202 +0,0 @@
/*
* 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;
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.config.CMSPublishConfig;
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.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.apache.commons.collections4.MapUtils;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.connection.stream.RecordId;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 栏目发布任务
*
* @author 兮玥
* @email 190785909@qq.com
*/
@RequiredArgsConstructor
@Component(IPublishTask.BeanPrefix + CatalogPublishTask.Type)
public class CatalogPublishTask implements IPublishTask<CmsCatalog> {
public final static String Type = "catalog";
private final ISiteService siteService;
private final ICatalogService catalogService;
private final IPublishPipeService publishPipeService;
private final ITemplateService templateService;
private final StaticizeService staticizeService;
private final StringRedisTemplate redisTemplate;
@Override
public String getType() {
return Type;
}
@Override
public void publish(CmsCatalog catalog) {
if (!catalog.isStaticize() || !catalog.isVisible() || CatalogType_Link.ID.equals(catalog.getCatalogType())) {
return;
}
String dataId = catalog.getCatalogId().toString();
MapRecord<String, String, String> record = MapRecord.create(CMSPublishConfig.PublishStreamName, Map.of(
"type", Type,
"id", dataId
));
redisTemplate.opsForStream().add(record);
}
@Override
public void staticize(Map<String, String> dataMap) {
Long catalogId = MapUtils.getLong(dataMap, "id");
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);
}
}
}
}

View File

@ -1,39 +0,0 @@
/*
* 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;
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);
}
}

View File

@ -1,262 +0,0 @@
/*
* 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;
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.config.CMSPublishConfig;
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.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.apache.commons.collections4.MapUtils;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.connection.stream.RecordId;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 内容发布任务
*
* @author 兮玥
* @email 190785909@qq.com
*/
@RequiredArgsConstructor
@Component(IPublishTask.BeanPrefix + ContentPublishTask.Type)
public class ContentPublishTask implements IPublishTask<CmsContent> {
public final static 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;
private final StringRedisTemplate redisTemplate;
@Override
public String getType() {
return Type;
}
@Override
public void publish(CmsContent content) {
CmsCatalog catalog = catalogService.getCatalog(content.getCatalogId());
if (!catalog.isStaticize() || content.isLinkContent()) {
return; // 不静态化直接跳过标题内容不需要静态化
}
CmsSite site = siteService.getSite(content.getSiteId());
List<CmsPublishPipe> publishPipes = publishPipeService.getPublishPipes(content.getSiteId());
// 判断内容是否有设置模板
boolean hasTemplate = false;
for (CmsPublishPipe pp : publishPipes) {
final String detailTemplate = getDetailTemplate(site, catalog, content, pp.getCode());
File templateFile = this.templateService.findTemplateFile(site, detailTemplate, pp.getCode());
if (Objects.nonNull(templateFile)) {
hasTemplate = true;
break;
}
}
if (!hasTemplate) {
return; // 没有可用模板不需要静态化
}
// 加入消息队列
String dataId = content.getContentId().toString();
MapRecord<String, String, String> record = MapRecord.create(CMSPublishConfig.PublishStreamName, Map.of(
"type", Type,
"id", dataId
));
redisTemplate.opsForStream().add(record);
}
@Override
public void staticize(Map<String, String> dataMap) {
Long contentId = MapUtils.getLong(dataMap, "id");
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;
}
}

View File

@ -1,48 +0,0 @@
/*
* 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;
/**
* IPublishStrategy
*
* @author 兮玥
* @email 190785909@qq.com
*/
public interface IPublishStrategy {
/**
* 发布策略ID
*/
String getId();
/**
* 创建发布任务
*
* @param dataType 数据类型
* @param dataId 数据ID
*/
void publish(String dataType, String dataId);
/**
* 获取待处理发布任务数量
*/
long getTaskCount();
/**
* 清空所有待处理发布任务
*/
void cleanTasks();
}

View File

@ -1,46 +0,0 @@
/*
* 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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
/**
* <TODO description class purpose>
*
* @author 兮玥
* @email 190785909@qq.com
*/
public interface IPublishTask<T> {
Logger logger = LoggerFactory.getLogger("publish");
String BeanPrefix = "PublishTask_";
String getType();
/**
* 创建发布任务
*/
void publish(T data);
/**
* 静态化
*/
void staticize(Map<String, String> dataMap);
}

View File

@ -1,30 +0,0 @@
/*
* 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;
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);
}

View File

@ -1,73 +0,0 @@
/*
* 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;
import com.chestnut.contentcore.config.CMSPublishConfig;
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 Map<String, IPublishTask<?>> publishTaskMap;
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");
IPublishTask<?> publishTask = publishTaskMap.get(IPublishTask.BeanPrefix + type);
if (Objects.nonNull(publishTask)) {
publishTask.staticize(map);
}
} catch(Exception e) {
logger.error("Publish err.", e);
} finally {
redisTemplate.opsForStream().acknowledge(stream, CMSPublishConfig.PublishConsumerGroup, message.getId().getValue());
redisTemplate.opsForStream().delete(stream, message.getId().getValue());
}
}
}
}

View File

@ -1,129 +0,0 @@
/*
* 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;
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.JacksonUtils;
import com.chestnut.common.utils.StringUtils;
import com.chestnut.contentcore.config.CMSPublishConfig;
import com.chestnut.contentcore.domain.CmsSite;
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.apache.commons.collections4.MapUtils;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.Map;
import java.util.Objects;
/**
* 站点发布任务
*
* @author 兮玥
* @email 190785909@qq.com
*/
@RequiredArgsConstructor
@Component(IPublishTask.BeanPrefix + SitePublishTask.Type)
public class SitePublishTask implements IPublishTask<CmsSite> {
public final static String Type = "site";
private final ISiteService siteService;
private final IPublishPipeService publishPipeService;
private final ITemplateService templateService;
private final StaticizeService staticizeService;
private final StringRedisTemplate redisTemplate;
@Override
public String getType() {
return Type;
}
@Override
public void publish(CmsSite site) {
String dataId = site.getSiteId().toString();
MapRecord<String, String, String> record = MapRecord.create(CMSPublishConfig.PublishStreamName, Map.of(
"type", Type,
"id", dataId
));
redisTemplate.opsForStream().add(record);
}
@Override
public void staticize(Map<String, String> dataMap) {
logger.info("开始发布:" + JacksonUtils.to(dataMap));
Long siteId = MapUtils.getLong(dataMap, "id");
if (IdUtils.validate(siteId)) {
CmsSite site = this.siteService.getSite(siteId);
if (Objects.nonNull(site)) {
this.siteStaticize(site);
}
}
logger.info("结束发布:" + JacksonUtils.to(dataMap));
}
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);
}
}
}

View File

@ -1,182 +0,0 @@
/*
* 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.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);
}
}
}
}

View File

@ -1,223 +0,0 @@
/*
* 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.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.dao().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;
}
}

View File

@ -1,110 +0,0 @@
/*
* 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.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);
}
}
}

View File

@ -1,80 +0,0 @@
/*
* 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());
}
}
}
}

View File

@ -1,114 +0,0 @@
/*
* 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.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);
}
}
}

View File

@ -1,135 +0,0 @@
/*
* 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.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;
}
}

View File

@ -1,90 +0,0 @@
/*
* 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.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;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 发布策略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.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 默认池满使用调用线程执行
executor.setAllowCoreThreadTimeOut(this.properties.getPool().isAllowCoreThreadTimeout());
executor.setWaitForTasksToCompleteOnShutdown(properties.getShutdown().isAwaitTermination());
executor.setAwaitTerminationSeconds((int) properties.getShutdown().getAwaitTerminationPeriod().toSeconds());
executor.initialize();
this.threadPoolTaskExecutor = executor;
}
}

View File

@ -1,38 +0,0 @@
package com.chestnut.exmodel.domain.dto;
import com.chestnut.exmodel.CmsExtendMetaModelType;
import com.chestnut.xmodel.domain.XModelField;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.Map;
@Getter
@Setter
public class XModelFieldDataDTO {
private String label;
private String fieldName;
private String controlType;
private List<Map<String, Object>> validations;
private List<Map<String, String>> options;
private Object value;
private String valueSrc;
public static XModelFieldDataDTO newInstance(XModelField field, String value) {
XModelFieldDataDTO dto = new XModelFieldDataDTO();
dto.setLabel(field.getName());
dto.setFieldName(CmsExtendMetaModelType.DATA_FIELD_PREFIX + field.getCode());
dto.setControlType(field.getControlType());
dto.setValidations(field.getValidations());
dto.setValue(value);
return dto;
}
}

View File

@ -1,14 +0,0 @@
package com.chestnut.cms.stat.core;
import com.chestnut.cms.stat.domain.CmsSiteVisitLog;
/**
* <TODO description class purpose>
*
* @author 兮玥
* @email 190785909@qq.com
*/
public interface CmsStat {
void deal(CmsSiteVisitLog log);
}

View File

@ -1,25 +0,0 @@
package com.chestnut.cms.stat.core.impl;
import com.chestnut.cms.stat.core.CmsStat;
import com.chestnut.cms.stat.domain.CmsSiteVisitLog;
import com.chestnut.contentcore.service.impl.ContentDynamicDataService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
/**
* 内容动态浏览量统计
*
* @author 兮玥
* @email 190785909@qq.com
*/
@Component
@RequiredArgsConstructor
public class CmsContentViewStat implements CmsStat {
private final ContentDynamicDataService contentDynamicDataService;
@Override
public void deal(final CmsSiteVisitLog log) {
this.contentDynamicDataService.increaseViewCount(log.getContentId());
}
}

View File

@ -1,32 +0,0 @@
package com.chestnut.common.async;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import com.chestnut.common.async.enums.TaskStatus;
import lombok.extern.slf4j.Slf4j;
/**
* 异步任务异常处理器
*
* @author 兮玥
* @email 190785909@qq.com
*/
@Slf4j
public class AsyncTaskExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
if (ArrayUtils.isNotEmpty(params) && params[0] instanceof AsyncTask task) {
task.setStatus(TaskStatus.FAILED);
task.setEndTime(LocalDateTime.now());
task.addErrorMessage(ex.getMessage());
AsyncTaskManager.setTaskMessage("Async task failed" + ex.getMessage());
log.error("Async task failed{}", ex.getMessage());
}
}
}

View File

@ -1,21 +0,0 @@
package com.chestnut.stat;
import java.util.List;
/**
* 统计类型
*
* <p>
* 每一个统计类型对应一种统计数据的存储及展示前端根据统计类型进行分类展示
* </p>
*
* @author 兮玥
* @email 190785909@qq.com
*/
public interface IStatType {
/**
* 统计菜单树
*/
public List<StatMenu> getStatMenus();
}

View File

@ -1,86 +0,0 @@
package com.chestnut.stat;
import java.time.LocalDateTime;
import com.chestnut.common.utils.IP2RegionUtils;
import com.chestnut.common.utils.ServletUtils;
import com.chestnut.common.utils.StringUtils;
import eu.bitwalker.useragentutils.UserAgent;
import jakarta.servlet.http.HttpServletRequest;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class RequestEvent {
/**
* 请求域
*/
private String host;
/**
* IP地址
*/
private String ip;
/**
* IP所属地区
*/
private String address;
/**
* 来源地址
*/
private String referer;
/**
* 浏览器类型
*/
private String browser;
/**
* UserAgent
*/
private String userAgent;
/**
* 操作系统
*/
private String os;
/**
* 设备类型
*/
private String deviceType;
/**
* 语言
*/
private String locale;
/**
* 事件发生时间
*/
private LocalDateTime evtTime;
/**
* 提取request请求信息到RequestEvent
*
* @param request
*/
public void fill(HttpServletRequest request) {
this.setHost(request.getRemoteHost());
this.setIp(ServletUtils.getIpAddr(request));
this.setAddress(IP2RegionUtils.ip2Region(this.getIp()));
this.setReferer(ServletUtils.getReferer(request));
this.setLocale(StringUtils.substringBefore(ServletUtils.getAcceptLanaguage(request), ","));
this.setUserAgent(ServletUtils.getUserAgent(request));
UserAgent ua = ServletUtils.parseUserAgent(request);
this.setBrowser(ua.getBrowser().getName());
this.setOs(ua.getOperatingSystem().getName());
this.setDeviceType(ua.getOperatingSystem().getDeviceType().getName());
}
}

View File

@ -1,11 +0,0 @@
package com.chestnut.stat;
/**
* 统计菜单
*
* @author 兮玥
* @email 190785909@qq.com
*/
public record StatMenu(String menuId, String parentId, String name, int sort) {
}

View File

@ -1,42 +0,0 @@
package com.chestnut.system.intercepter;
import java.util.Objects;
import org.springframework.http.HttpMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import com.chestnut.common.security.SecurityUtils;
import com.chestnut.common.security.exception.SecurityErrorCode;
import com.chestnut.system.annotation.IgnoreDemoMode;
import com.chestnut.system.security.StpAdminUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
/**
* 演示模式拦截器
*/
@RequiredArgsConstructor
public class DemoModeIntercepter implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String method = request.getMethod();
if (StpAdminUtil.isLogin() && SecurityUtils.isSuperAdmin(StpAdminUtil.getLoginIdAsLong())) {
return true; // 超级管理员允许操作
}
// 非Get且无忽略演示模式注解则抛出异常
if (HttpMethod.POST.matches(method) || HttpMethod.PUT.matches(method) || HttpMethod.DELETE.matches(method)) {
if (handler instanceof HandlerMethod handlerMethod) {
IgnoreDemoMode ignoreDemoMode = handlerMethod.getMethodAnnotation(IgnoreDemoMode.class);
if (Objects.isNull(ignoreDemoMode)) {
throw SecurityErrorCode.DEMO_EXCEPTION.exception();
}
}
}
return true;
}
}

View File

@ -1,67 +0,0 @@
/*
* 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.system.mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
public interface DataBaseMapper {
/**
* 表字段是否存在
*
* @param tableName
* @param columnName
* @return
*/
@Select("select count(*) from information_schema.columns where table_name = #{tableName} and column_name = #{columnName}")
public Integer isTableColumnExists(@Param("tableName") String tableName, @Param("columnName") String columnName);
/**
* 添加表字段
*
* @param tableName 表明
* @param columnName 字段名
* @param columnType 字段类型
*/
@Update("alter table #{tableName} add column #{columnName} #{columnType}")
public void addTableColumn(@Param("tableName") String tableName, @Param("columnName") String columnName,
@Param("columnType") String columnType);
/**
* 重命名表
*
* @param oldName
* @param newName
*/
@Update("alter table #{oldTableName} rename #{newTableName}")
public void renameTable(String oldTableName, String newTableName);
/**
* 清空表数据自增清零
*
* @param tableName
*/
@Update("truncate table ${tableName}")
public void truncateTable(@Param("tableName") String tableName);
/**
* 当前数据库名
*/
@Select("select database()")
public void getDatabaseName();
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB