版本更新:V1.5.3

This commit is contained in:
liweiyi 2025-03-04 19:00:41 +08:00
parent df1edc879f
commit 0274696789
115 changed files with 3156 additions and 2240 deletions

View File

@ -1,4 +1,4 @@
# ChestnutCMS v1.5.2
# ChestnutCMS v1.5.3
### 系统简介
@ -6,7 +6,7 @@ ChestnutCMS是前后端分离的企业级内容管理系统。项目基于[RuoYi
### 系统预览
后台预览地址:<http://admin.1000mz.com>
后台预览地址:<https://admin.1000mz.com>
账号demo / a123456

View File

@ -3,7 +3,7 @@
<parent>
<artifactId>chestnut</artifactId>
<groupId>com.chestnut</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>

View File

@ -5,7 +5,7 @@ chestnut:
# 代号
alias: ChestnutCMS
# 版本
version: 1.5.2
version: 1.5.3
# 版权年份
copyrightYear: 2022-2024
system:

View File

@ -5,7 +5,7 @@ chestnut:
# 代号
alias: ChestnutCMS
# 版本
version: 1.5.2
version: 1.5.3
# 版权年份
copyrightYear: 2022-2024
system:

View File

@ -5,7 +5,7 @@ chestnut:
# 代号
alias: ChestnutCMS
# 版本
version: 1.5.2
version: 1.5.3
# 版权年份
copyrightYear: 2022-2024
system:

View File

@ -0,0 +1,15 @@
ALTER TABLE cms_ad_click_log ADD COLUMN `ad_name` varchar(255);
ALTER TABLE cms_ad_click_log MODIFY COLUMN `ip` varchar(40);
ALTER TABLE cms_ad_click_log MODIFY COLUMN `address` varchar(100);
ALTER TABLE cms_ad_click_log MODIFY COLUMN `browser` varchar(50);
ALTER TABLE cms_ad_click_log MODIFY COLUMN `os` varchar(50);
ALTER TABLE cms_ad_click_log MODIFY COLUMN `device_type` varchar(50);
ALTER TABLE cms_ad_click_log MODIFY COLUMN `locale` varchar(20);
ALTER TABLE cms_ad_view_log ADD COLUMN `ad_name` varchar(255);
ALTER TABLE cms_ad_view_log MODIFY COLUMN `ip` varchar(40);
ALTER TABLE cms_ad_view_log MODIFY COLUMN `address` varchar(100);
ALTER TABLE cms_ad_view_log MODIFY COLUMN `browser` varchar(50);
ALTER TABLE cms_ad_view_log MODIFY COLUMN `os` varchar(50);
ALTER TABLE cms_ad_view_log MODIFY COLUMN `device_type` varchar(50);
ALTER TABLE cms_ad_view_log MODIFY COLUMN `locale` varchar(20);

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-cms</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<artifactId>chestnut-cms-advertisement</artifactId>

View File

@ -37,6 +37,5 @@ public class AdSpacePageWidget extends AbstractPageWidget {
// 删除广告版位相关的广告
this.advertisementService.remove(new LambdaQueryWrapper<CmsAdvertisement>()
.eq(CmsAdvertisement::getAdSpaceId, this.getPageWidgetEntity().getPageWidgetId()));
// TODO 删除广告统计数据
}
}

View File

@ -0,0 +1,82 @@
/*
* 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.advertisement;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chestnut.advertisement.domain.CmsAdvertisement;
import com.chestnut.advertisement.service.IAdvertisementService;
import com.chestnut.common.async.AsyncTaskManager;
import com.chestnut.contentcore.core.IResourceStat;
import com.chestnut.contentcore.core.InternalURL;
import com.chestnut.contentcore.util.InternalUrlUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 自定义区块资源引用统计
*
* @author 兮玥
* @email 190785909@qq.com
*/
@RequiredArgsConstructor
@Component(IResourceStat.BEAN_PREFIX + AdvResourceStat.TYPE)
public class AdvResourceStat implements IResourceStat {
public static final String TYPE = "Adv";
private final IAdvertisementService advertisementService;
@Override
public String getType() {
return TYPE;
}
@Override
public void statQuotedResource(Long siteId, Map<Long, Long> quotedResources) throws InterruptedException {
int pageSize = 1000;
long lastId = 0L;
int count = 0;
long total = advertisementService.lambdaQuery().select(List.of(CmsAdvertisement::getAdvertisementId))
.eq(CmsAdvertisement::getSiteId, siteId).count();
while (true) {
LambdaQueryWrapper<CmsAdvertisement> q = new LambdaQueryWrapper<CmsAdvertisement>()
.select(List.of(CmsAdvertisement::getResourcePath))
.eq(CmsAdvertisement::getSiteId, siteId)
.gt(CmsAdvertisement::getAdvertisementId, lastId)
.orderByAsc(CmsAdvertisement::getAdvertisementId);
Page<CmsAdvertisement> page = advertisementService.page(new Page<>(0, pageSize, false), q);
for (CmsAdvertisement advertisement : page.getRecords()) {
AsyncTaskManager.setTaskProgressInfo((int) (count * 100 / total),
"正在统计广告资源引用:" + count + " / " + total + "]");
lastId = advertisement.getAdvertisementId();
InternalURL internalURL = InternalUrlUtils.parseInternalUrl(advertisement.getResourcePath());
if (Objects.nonNull(internalURL)) {
quotedResources.compute(internalURL.getId(), (k, v) -> Objects.isNull(v) ? 1 : v + 1);
}
AsyncTaskManager.checkInterrupt();
count++;
}
if (page.getRecords().size() < pageSize) {
break;
}
}
}
}

View File

@ -18,6 +18,7 @@ package com.chestnut.advertisement.controller.front;
import com.chestnut.advertisement.stat.AdClickStatEventHandler;
import com.chestnut.advertisement.stat.AdViewStatEventHandler;
import com.chestnut.common.security.web.BaseRestController;
import com.chestnut.common.utils.IdUtils;
import com.chestnut.common.utils.JacksonUtils;
import com.chestnut.common.utils.ServletUtils;
import com.chestnut.stat.core.StatEvent;
@ -61,6 +62,10 @@ public class AdApiController extends BaseRestController {
@GetMapping("/click")
public void adClick(@RequestParam("sid") Long siteId, @RequestParam("aid") Long advertisementId) {
if (!IdUtils.validate(siteId) || !IdUtils.validate(advertisementId)) {
log.warn("Invalid sid/aid: sid = {}, aid = {}", siteId, advertisementId);
return;
}
StatEvent evt = new StatEvent();
evt.setType(AdClickStatEventHandler.TYPE);
ObjectNode objectNode = JacksonUtils.objectNode();
@ -74,6 +79,10 @@ public class AdApiController extends BaseRestController {
@GetMapping("/view")
public void adView(@RequestParam("sid") Long siteId, @RequestParam("aid") Long advertisementId) {
if (!IdUtils.validate(siteId) || !IdUtils.validate(advertisementId)) {
log.warn("Invalid sid/aid: sid = {}, aid = {}", siteId, advertisementId);
return;
}
StatEvent evt = new StatEvent();
evt.setType(AdViewStatEventHandler.TYPE);
ObjectNode objectNode = JacksonUtils.objectNode();

View File

@ -52,7 +52,6 @@ public class CmsAdClickLog implements Serializable {
/**
* 广告名称
*/
@TableField(exist = false)
private String adName;
/**

View File

@ -52,7 +52,6 @@ public class CmsAdViewLog implements Serializable {
/**
* 广告名称
*/
@TableField(exist = false)
private String adName;
/**

View File

@ -16,25 +16,33 @@
package com.chestnut.advertisement.stat;
import com.chestnut.advertisement.domain.CmsAdClickLog;
import com.chestnut.advertisement.service.IAdvertisementService;
import com.chestnut.advertisement.service.IAdvertisementStatService;
import com.chestnut.common.utils.IdUtils;
import com.chestnut.stat.core.IStatEventHandler;
import com.chestnut.stat.core.StatEvent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.Objects;
/**
* 广告点击事件
*
* @author 兮玥
* @email 190785909@qq.com
*/
@Slf4j
@RequiredArgsConstructor
@Component(IStatEventHandler.BEAN_PREFIX + AdClickStatEventHandler.TYPE)
public class AdClickStatEventHandler implements IStatEventHandler {
public static final String TYPE = "adclick";
private final IAdvertisementService adService;
private final IAdvertisementStatService advertisementStatService;
@Override
@ -44,15 +52,23 @@ public class AdClickStatEventHandler implements IStatEventHandler {
@Override
public void handle(StatEvent event) {
CmsAdClickLog log = parseLog(event);
String aid = event.getData().get("aid").asText();
Map<String, String> map = this.adService.getAdvertisementMap();
String adName = map.get(aid);
if (Objects.isNull(adName)) {
log.warn("Invalid aid: {}", aid);
return;
}
CmsAdClickLog log = parseLog(Long.parseLong(aid), adName, event);
advertisementStatService.adClick(log);
}
private CmsAdClickLog parseLog(StatEvent event) {
private CmsAdClickLog parseLog(long advertiseId, String adName, StatEvent event) {
CmsAdClickLog log = new CmsAdClickLog();
log.setLogId(IdUtils.getSnowflakeId());
log.setSiteId(event.getData().get("sid").asLong());
log.setAdId(event.getData().get("aid").asLong());
log.setAdId(advertiseId);
log.setAdName(adName);
log.setHost(event.getRequestData().getHost());
log.setIp(event.getRequestData().getIp());

View File

@ -16,25 +16,33 @@
package com.chestnut.advertisement.stat;
import com.chestnut.advertisement.domain.CmsAdViewLog;
import com.chestnut.advertisement.service.IAdvertisementService;
import com.chestnut.advertisement.service.IAdvertisementStatService;
import com.chestnut.common.utils.IdUtils;
import com.chestnut.stat.core.IStatEventHandler;
import com.chestnut.stat.core.StatEvent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.Objects;
/**
* 广告展现事件
*
* @author 兮玥
* @email 190785909@qq.com
*/
@Slf4j
@RequiredArgsConstructor
@Component(IStatEventHandler.BEAN_PREFIX + AdViewStatEventHandler.TYPE)
public class AdViewStatEventHandler implements IStatEventHandler {
public static final String TYPE = "adview";
private final IAdvertisementService adService;
private final IAdvertisementStatService advertisementStatService;
@Override
@ -44,15 +52,23 @@ public class AdViewStatEventHandler implements IStatEventHandler {
@Override
public void handle(StatEvent event) {
CmsAdViewLog log = parseLog(event);
String aid = event.getData().get("aid").asText();
Map<String, String> map = this.adService.getAdvertisementMap();
String adName = map.get(aid);
if (Objects.isNull(adName)) {
log.warn("Invalid aid: {}", aid);
return;
}
CmsAdViewLog log = parseLog(Long.parseLong(aid), adName, event);
advertisementStatService.adView(log);
}
private CmsAdViewLog parseLog(StatEvent event) {
private CmsAdViewLog parseLog(long advertiseId, String adName, StatEvent event) {
CmsAdViewLog log = new CmsAdViewLog();
log.setLogId(IdUtils.getSnowflakeId());
log.setSiteId(event.getData().get("sid").asLong());
log.setAdId(event.getData().get("aid").asLong());
log.setAdId(advertiseId);
log.setAdName(adName);
log.setHost(event.getRequestData().getHost());
log.setIp(event.getRequestData().getIp());

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-cms</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<artifactId>chestnut-cms-article</artifactId>

View File

@ -0,0 +1,98 @@
/*
* 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.article;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chestnut.article.domain.CmsArticleDetail;
import com.chestnut.article.format.ArticleBodyFormat_RichText;
import com.chestnut.article.service.IArticleService;
import com.chestnut.common.async.AsyncTaskManager;
import com.chestnut.contentcore.core.IResourceStat;
import com.chestnut.contentcore.core.InternalURL;
import com.chestnut.contentcore.util.InternalUrlUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
/**
* 文章正文资源引用统计
*
* @author 兮玥
* @email 190785909@qq.com
*/
@Slf4j
@RequiredArgsConstructor
@Component(IResourceStat.BEAN_PREFIX + ArticleRichTextResourceStat.TYPE)
public class ArticleRichTextResourceStat implements IResourceStat {
public static final String TYPE = "ArticleRichText";
private final IArticleService articleService;
@Override
public String getType() {
return TYPE;
}
@Override
public void statQuotedResource(Long siteId, Map<Long, Long> quotedResources) throws InterruptedException {
int pageSize = 100;
long lastId = 0L;
int count = 0;
long total = articleService.dao().lambdaQuery().select(List.of(CmsArticleDetail::getContentId))
.eq(CmsArticleDetail::getSiteId, siteId)
.eq(CmsArticleDetail::getFormat, ArticleBodyFormat_RichText.ID)
.count();
while (true) {
LambdaQueryWrapper<CmsArticleDetail> q = new LambdaQueryWrapper<CmsArticleDetail>()
.select(List.of(CmsArticleDetail::getContentHtml))
.eq(CmsArticleDetail::getSiteId, siteId)
.eq(CmsArticleDetail::getFormat, ArticleBodyFormat_RichText.ID)
.gt(CmsArticleDetail::getContentId, lastId)
.orderByAsc(CmsArticleDetail::getContentId);
Page<CmsArticleDetail> page = articleService.dao().page(new Page<>(0, pageSize, false), q);
for (CmsArticleDetail articleDetail : page.getRecords()) {
AsyncTaskManager.setTaskProgressInfo((int) (count * 100 / total),
"正在统计富文本文章正文资源引用:" + count + " / " + total + "]");
lastId = articleDetail.getContentId();
// 解析文章正文
Matcher matcher = InternalUrlUtils.InternalUrlTagPattern.matcher(articleDetail.getContentHtml());
while (matcher.find()) {
String iurl = matcher.group(1);
try {
InternalURL internalUrl = InternalUrlUtils.parseInternalUrl(iurl);
if (Objects.nonNull(internalUrl)) {
quotedResources.compute(internalUrl.getId(), (k, v) -> Objects.isNull(v) ? 1 : v + 1);
}
} catch (Exception e) {
log.warn("InternalUrl parse failed: " + iurl, e);
}
}
AsyncTaskManager.checkInterrupt();
count++;
}
if (page.getRecords().size() < pageSize) {
break;
}
}
}
}

View File

@ -24,7 +24,7 @@ import lombok.Setter;
import java.util.Objects;
/**
* <TODO description class purpose>
* 文章内容API接口VO
*
* @author 兮玥
* @email 190785909@qq.com

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-cms</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<artifactId>chestnut-cms-block</artifactId>

View File

@ -0,0 +1,96 @@
/*
* 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.block;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chestnut.common.async.AsyncTaskManager;
import com.chestnut.common.utils.JacksonUtils;
import com.chestnut.common.utils.StringUtils;
import com.chestnut.contentcore.core.IResourceStat;
import com.chestnut.contentcore.core.InternalURL;
import com.chestnut.contentcore.domain.CmsPageWidget;
import com.chestnut.contentcore.service.IPageWidgetService;
import com.chestnut.contentcore.util.InternalUrlUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 自定义区块资源引用统计
*
* @author 兮玥
* @email 190785909@qq.com
*/
@RequiredArgsConstructor
@Component(IResourceStat.BEAN_PREFIX + BlockResourceStat.TYPE)
public class BlockResourceStat implements IResourceStat {
public static final String TYPE = "Block";
private final IPageWidgetService pageWidgetService;
private final ManualPageWidgetType manualPageWidgetType;
@Override
public String getType() {
return TYPE;
}
@Override
public void statQuotedResource(Long siteId, Map<Long, Long> quotedResources) throws InterruptedException {
int pageSize = 1000;
long lastId = 0L;
int count = 0;
long total = pageWidgetService.lambdaQuery().select(List.of(CmsPageWidget::getContent))
.eq(CmsPageWidget::getSiteId, siteId).eq(CmsPageWidget::getType, manualPageWidgetType.getId()).count();
while (true) {
LambdaQueryWrapper<CmsPageWidget> q = new LambdaQueryWrapper<CmsPageWidget>()
.select(List.of(CmsPageWidget::getContent))
.eq(CmsPageWidget::getSiteId, siteId)
.eq(CmsPageWidget::getType, manualPageWidgetType.getId())
.gt(CmsPageWidget::getPageWidgetId, lastId)
.orderByAsc(CmsPageWidget::getPageWidgetId);
Page<CmsPageWidget> page = pageWidgetService.page(new Page<>(0, pageSize, false), q);
for (CmsPageWidget pageWidget : page.getRecords()) {
AsyncTaskManager.setTaskProgressInfo((int) (count * 100 / total),
"正在统计自定义区块资源引用:" + count + " / " + total + "]");
lastId = pageWidget.getPageWidgetId();
if (StringUtils.isNotEmpty(pageWidget.getContent())) {
List<ManualPageWidgetType.RowData> rowData = JacksonUtils.fromList(pageWidget.getContent(), ManualPageWidgetType.RowData.class);
if (Objects.nonNull(rowData)) {
rowData.forEach(row -> {
row.getItems().forEach(item -> {
InternalURL internalURL = InternalUrlUtils.parseInternalUrl(item.getLogo());
if (Objects.nonNull(internalURL)) {
quotedResources.compute(internalURL.getId(), (k, v) -> Objects.isNull(v) ? 1 : v + 1);
}
});
});
}
}
AsyncTaskManager.checkInterrupt();
count++;
}
if (page.getRecords().size() < pageSize) {
break;
}
}
}
}

View File

@ -96,7 +96,7 @@ public class ManualPageWidgetType implements IPageWidgetType {
list = List.of();
}
list.forEach(rd -> rd.getItems().forEach(item -> {
item.setLogoSrc(InternalUrlUtils.getActualPreviewUrl(item.logo));
item.setLogoSrc(InternalUrlUtils.getActualUrl(item.logo, publishPipeCode, isPreview));
item.setLink(item.getUrl());
}));
return list;

View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-cms</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<artifactId>chestnut-cms-comment</artifactId>

View File

@ -16,7 +16,7 @@
package com.chestnut.cms.comment;
/**
* <TODO description class purpose>
* 评论模块静态变量
*
* @author 兮玥
* @email 190785909@qq.com

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-cms</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<artifactId>chestnut-cms-contentcore</artifactId>

View File

@ -146,8 +146,8 @@ public class CoreController extends BaseRestController {
templateType.initTemplateData(siteId, templateContext);
templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, params);
// TODO 兼容历史版本下个大版本移除IncludeRequest模板变量
templateContext.getVariables().put("IncludeRequest", params);
templateContext.getVariables().put("ClientType", ServletUtils.getDeviceType());
templateContext.getVariables().put(TemplateUtils.TemplateVariable_IncludeRequest, params);
templateContext.getVariables().put(TemplateUtils.TemplateVariable_ClientType, ServletUtils.getDeviceType());
// staticize
HttpServletResponse response = ServletUtils.getResponse();
response.setCharacterEncoding(Charset.defaultCharset().displayName());

View File

@ -55,13 +55,19 @@ import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.apache.commons.io.FileUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -388,30 +394,31 @@ public class SiteController extends BaseRestController {
}
@Priv(type = AdminUserType.TYPE, value = "Site:Edit:${#siteId}")
@PostMapping("/theme_download")
public void export(@RequestParam Long siteId, HttpServletResponse response) throws IOException {
@GetMapping("/downloadTheme/{siteId}")
public ResponseEntity<StreamingResponseBody> downloadTheme(@PathVariable @LongId Long siteId) {
CmsSite site = this.siteService.getSite(siteId);
File file = new File(SiteUtils.getSiteResourceRoot(site) + SiteThemeService.ThemeZipPath);
if (!file.exists()) {
response.getWriter().write("站点主题文件不存在");
return;
}
response.setContentType("application/octet-stream");
response.setCharacterEncoding(StandardCharsets.UTF_8.displayName());
response.setHeader("Content-disposition", "attachment;filename="
+ StringUtils.substringAfterLast(SiteThemeService.ThemeZipPath, "/"));
response.addHeader("Content-Length", "" + file.length());
try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
byte[] buff = new byte[1024];
OutputStream os = response.getOutputStream();
int i;
while ((i = bis.read(buff)) != -1) {
os.write(buff, 0, i);
os.flush();
}
} catch (IOException e) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.getWriter().write("Export site theme file failed: " + e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getName() + "\"")
.contentLength(file.length())
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(outputStream -> {
long timeout = 600_000;
Instant startTime = Instant.now();
try (InputStream is = new FileInputStream(file)) {
byte[] data = new byte[8192];
int bytesRead;
while ((bytesRead = is.read(data)) != -1) {
outputStream.write(data, 0, bytesRead);
outputStream.flush();
if (Duration.between(startTime, Instant.now()).toMillis() > timeout) {
throw new RuntimeException("Timed out");
}
}
}
});
}
}

View File

@ -25,12 +25,12 @@ import com.chestnut.contentcore.domain.CmsCatalog;
import com.chestnut.contentcore.domain.CmsContent;
import com.chestnut.contentcore.domain.vo.ContentApiVO;
import com.chestnut.contentcore.domain.vo.ContentDynamicDataVO;
import com.chestnut.contentcore.domain.vo.ContentVO;
import com.chestnut.contentcore.fixed.dict.ContentAttribute;
import com.chestnut.contentcore.fixed.dict.ContentStatus;
import com.chestnut.contentcore.service.ICatalogService;
import com.chestnut.contentcore.service.IContentService;
import com.chestnut.contentcore.service.impl.ContentDynamicDataService;
import com.chestnut.contentcore.template.tag.CmsContentTag;
import com.chestnut.contentcore.util.CatalogUtils;
import com.chestnut.contentcore.util.InternalUrlUtils;
import lombok.RequiredArgsConstructor;
@ -91,21 +91,22 @@ public class ContentApiController extends BaseRestController {
@RequestParam(value = "pp") String publishPipeCode,
@RequestParam(value = "preview", required = false, defaultValue = "false") Boolean preview
) {
if (!"Root".equalsIgnoreCase(level) && !IdUtils.validate(catalogId)) {
if (!CmsContentTag.LevelTagAttr.isRoot(level) && !IdUtils.validate(catalogId)) {
return R.fail("The parameter cid is required where lv is `Root`.");
}
LambdaQueryWrapper<CmsContent> q = new LambdaQueryWrapper<>();
q.eq(CmsContent::getSiteId, siteId).eq(CmsContent::getStatus, ContentStatus.PUBLISHED);
if (!"Root".equalsIgnoreCase(level)) {
if (!CmsContentTag.LevelTagAttr.isRoot(level)) {
CmsCatalog catalog = this.catalogService.getCatalog(catalogId);
if (Objects.isNull(catalog)) {
return R.fail("Catalog not found: " + catalogId);
}
if ("Current".equalsIgnoreCase(level)) {
if (CmsContentTag.LevelTagAttr.isCurrent(level)) {
q.eq(CmsContent::getCatalogId, catalog.getCatalogId());
} else if ("Child".equalsIgnoreCase(level)) {
} else if (CmsContentTag.LevelTagAttr.isChild(level)) {
q.likeRight(CmsContent::getCatalogAncestors, catalog.getAncestors() + CatalogUtils.ANCESTORS_SPLITER);
} else if ("CurrentAndChild".equalsIgnoreCase(level)) {
} else if (CmsContentTag.LevelTagAttr.isCurrentAndChild(level)) {
q.likeRight(CmsContent::getCatalogAncestors, catalog.getAncestors());
}
}
@ -121,8 +122,10 @@ public class ContentApiController extends BaseRestController {
q.apply(bit > 0, "attributes&{0}<>{1}", attrTotal, bit);
}
}
if ("Recent".equalsIgnoreCase(sortType)) {
if (CmsContentTag.SortTagAttr.isRecent(sortType)) {
q.orderByDesc(CmsContent::getPublishDate);
} else if (CmsContentTag.SortTagAttr.isViews(sortType)) {
q.orderByDesc(CmsContent::getViewCount);
} else {
q.orderByDesc(Arrays.asList(CmsContent::getTopFlag, CmsContent::getSortFlag));
}
@ -143,12 +146,4 @@ public class ContentApiController extends BaseRestController {
});
return R.ok(list);
}
/**
* TODO 按浏览量排序获取数据
*/
public R<ContentVO> getHotContentList() {
return R.ok();
}
}

View File

@ -27,6 +27,7 @@ import com.chestnut.contentcore.exception.ContentCoreErrorCode;
import com.chestnut.contentcore.fixed.dict.ContentOpType;
import com.chestnut.contentcore.fixed.dict.ContentStatus;
import com.chestnut.contentcore.listener.event.*;
import com.chestnut.contentcore.perms.CatalogPermissionType;
import com.chestnut.contentcore.properties.PublishedContentEditProperty;
import com.chestnut.contentcore.service.ICatalogService;
import com.chestnut.contentcore.service.IContentService;
@ -37,6 +38,7 @@ import com.chestnut.contentcore.util.ContentCoreUtils;
import com.chestnut.contentcore.util.ContentLogUtils;
import com.chestnut.contentcore.util.InternalUrlUtils;
import com.chestnut.system.fixed.dict.YesOrNo;
import com.chestnut.system.permission.PermissionUtils;
import lombok.Setter;
import org.springframework.beans.BeanUtils;
@ -270,7 +272,9 @@ public abstract class AbstractContent<T> implements IContent<T> {
this.getContentEntity().getTitle())) {
throw ContentCoreErrorCode.TITLE_REPLEAT.exception();
}
// TODO 校验权限需要同时拥有目标栏目的新建权限和源栏目的复制权限
// 校验权限
PermissionUtils.checkPermission(CatalogPermissionType.CatalogPrivItem.AddContent.getPermissionKey(toCatalog.getCatalogId()), this.getOperator());
CmsContent newContent = new CmsContent();
BeanUtils.copyProperties(this.getContentEntity(), newContent, "contentId", "template", "staticPath", "topFlag",
"topDate", "isLock", "lockUser");
@ -310,7 +314,9 @@ public abstract class AbstractContent<T> implements IContent<T> {
this.getContentEntity().getTitle())) {
throw ContentCoreErrorCode.TITLE_REPLEAT.exception();
}
// TODO 校验权限需要同时拥有目标栏目的新建权限和源栏目的转移权限
// 校验权限
PermissionUtils.checkPermission(CatalogPermissionType.CatalogPrivItem.AddContent.getPermissionKey(toCatalog.getCatalogId()), this.getOperator());
CmsCatalog fromCatalog = this.getCatalogService().getCatalog(content.getCatalogId());
// 重置内容信息
content.setSiteId(toCatalog.getSiteId());

View File

@ -25,8 +25,15 @@ import java.util.Map;
*/
public interface IResourceStat {
String BEAN_PREFIX = "ResourceStat_";
/**
* 查找资源引用次数
* 引用分类
*/
Map<Long, Integer> findQuotedResource();
String getType();
/**
* 统计资源引用次数
*/
void statQuotedResource(Long siteId, Map<Long, Long> quotedResources) throws InterruptedException;
}

View File

@ -16,7 +16,7 @@
package com.chestnut.contentcore.core;
/**
* <TODO description class purpose>
* 站点主题导入/导出上下文
*
* @author 兮玥
* @email 190785909@qq.com

View File

@ -15,6 +15,10 @@
*/
package com.chestnut.contentcore.core.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chestnut.common.async.AsyncTaskManager;
import com.chestnut.common.utils.ArrayUtils;
import com.chestnut.contentcore.core.IResourceStat;
import com.chestnut.contentcore.core.InternalURL;
import com.chestnut.contentcore.domain.CmsCatalog;
@ -27,18 +31,22 @@ import com.chestnut.contentcore.util.InternalUrlUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* <TODO description class purpose>
* 资源引用统计
*
* @author 兮玥
* @email 190785909@qq.com
*/
@Component
@RequiredArgsConstructor
@Component(IResourceStat.BEAN_PREFIX + ContentCoreResourceStat.TYPE)
public class ContentCoreResourceStat implements IResourceStat {
public static final String TYPE = "ContentCore";
private final ISiteService siteService;
private final ICatalogService catalogService;
@ -46,60 +54,93 @@ public class ContentCoreResourceStat implements IResourceStat {
private final IContentService contentService;
@Override
public Map<Long, Integer> findQuotedResource() {
Map<Long, Integer> quotedResources = new HashMap<>();
this.siteLogo().forEach(rid -> {
quotedResources.compute(rid, (k, v) -> {
return Objects.isNull(v) ? 1 : v++;
});
});
public String getType() {
return TYPE;
}
this.catalogLogo();
this.contentLogo();
return quotedResources;
@Override
public void statQuotedResource(Long siteId, Map<Long, Long> quotedResources) throws InterruptedException {
this.siteLogo(siteId, quotedResources);
this.catalogLogo(siteId, quotedResources);
this.contentLogo(siteId, quotedResources);
}
/**
* 内容logo引用
*/
private Set<Long> contentLogo() {
Set<Long> resourceIds = new HashSet<>();
this.contentService.dao().lambdaQuery().select(List.of(CmsContent::getImages)).list().forEach(content -> {
if (Objects.nonNull(content.getImages())) {
InternalURL internalURL = InternalUrlUtils.parseInternalUrl(content.getLogo());
if (Objects.nonNull(internalURL)) {
resourceIds.add(internalURL.getId());
}
private void contentLogo(Long siteId, Map<Long, Long> quotedResources) throws InterruptedException {
int pageSize = 1000;
long lastContentId = 0L;
int count = 0;
long total = contentService.dao().lambdaQuery().select(List.of(CmsContent::getImages))
.eq(CmsContent::getSiteId, siteId).isNotNull(CmsContent::getImages).count();
while (true) {
LambdaQueryWrapper<CmsContent> q = new LambdaQueryWrapper<CmsContent>()
.select(List.of(CmsContent::getImages))
.eq(CmsContent::getSiteId, siteId)
.isNotNull(CmsContent::getImages)
.gt(CmsContent::getContentId, lastContentId)
.orderByAsc(CmsContent::getContentId);
Page<CmsContent> page = contentService.dao().page(new Page<>(0, pageSize, false), q);
for (CmsContent content : page.getRecords()) {
AsyncTaskManager.setTaskProgressInfo((int) (count * 100 / total),
"正在统计内容LOGO资源引用" + count + " / " + total + "]");
lastContentId = content.getContentId();
ArrayUtils.mapNotNull(content.getImages(), InternalUrlUtils::parseInternalUrl)
.forEach(internalURL -> {
quotedResources.compute(internalURL.getId(), (k, v) -> Objects.isNull(v) ? 1 : v + 1);
});
AsyncTaskManager.checkInterrupt();
count++;
}
});
return resourceIds;
if (page.getRecords().size() < pageSize) {
break;
}
}
}
/**
* 栏目logo引用
*/
private Set<Long> catalogLogo() {
Set<Long> resourceIds = new HashSet<>();
this.catalogService.lambdaQuery().select(List.of(CmsCatalog::getLogo)).list().forEach(catalog -> {
InternalURL internalURL = InternalUrlUtils.parseInternalUrl(catalog.getLogo());
if (Objects.nonNull(internalURL)) {
resourceIds.add(internalURL.getId());
private void catalogLogo(Long siteId, Map<Long, Long> quotedResources) throws InterruptedException {
int pageSize = 1000;
long lastCatalogId = 0L;
int count = 0;
long total = catalogService.lambdaQuery().select(List.of(CmsCatalog::getLogo))
.eq(CmsCatalog::getSiteId, siteId).isNotNull(CmsCatalog::getLogo).count();
while (true) {
LambdaQueryWrapper<CmsCatalog> q = new LambdaQueryWrapper<CmsCatalog>()
.select(List.of(CmsCatalog::getLogo))
.eq(CmsCatalog::getSiteId, siteId)
.isNotNull(CmsCatalog::getLogo)
.gt(CmsCatalog::getCatalogId, lastCatalogId)
.orderByAsc(CmsCatalog::getCatalogId);
Page<CmsCatalog> page = catalogService.page(new Page<>(0, pageSize, false), q);
for (CmsCatalog catalog : page.getRecords()) {
AsyncTaskManager.setTaskProgressInfo((int) (count * 100 / total),
"正在统计栏目LOGO资源引用" + count + " / " + total + "]");
lastCatalogId = catalog.getCatalogId();
InternalURL internalURL = InternalUrlUtils.parseInternalUrl(catalog.getLogo());
if (Objects.nonNull(internalURL)) {
quotedResources.compute(internalURL.getId(), (k, v) -> Objects.isNull(v) ? 1 : v + 1);
}
AsyncTaskManager.checkInterrupt();
count++;
}
});
return resourceIds;
if (page.getRecords().size() < pageSize) {
break;
}
}
}
/**
* 站点logo引用
*/
private Set<Long> siteLogo() {
Set<Long> resourceIds = new HashSet<>();
this.siteService.lambdaQuery().select(List.of(CmsSite::getLogo)).list().forEach(site -> {
if (InternalUrlUtils.isInternalUrl(site.getLogo())) {
InternalURL internalURL = InternalUrlUtils.parseInternalUrl(site.getLogo());
resourceIds.add(internalURL.getId());
}
});
return resourceIds;
private void siteLogo(Long siteId, Map<Long, Long> quotedResources) {
CmsSite site = this.siteService.getById(siteId);
InternalURL internalURL = InternalUrlUtils.parseInternalUrl(site.getLogo());
if (Objects.nonNull(internalURL)) {
quotedResources.compute(internalURL.getId(), (k, v) -> Objects.isNull(v) ? 1 : v + 1);
}
}
}

View File

@ -0,0 +1,64 @@
/*
* 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.core.impl;
import com.chestnut.common.utils.StringUtils;
import com.chestnut.contentcore.core.IPublishPipeProp;
import org.apache.commons.collections4.MapUtils;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 发布通道属性通用错误页面地址
*
* @author 兮玥
* @email 190785909@qq.com
*/
@Component(IPublishPipeProp.BEAN_PREFIX + PublishPipeProp_ErrPageLink.KEY)
public class PublishPipeProp_ErrPageLink implements IPublishPipeProp {
public static final String KEY = "errPageLink";
@Override
public String getKey() {
return KEY;
}
@Override
public String getName() {
return "通用错误页面链接";
}
@Override
public List<PublishPipePropUseType> getUseTypes() {
return List.of(PublishPipePropUseType.Site);
}
@Override
public String getDefaultValue() {
return StringUtils.SLASH;
}
public static String getValue(String publishPipeCode, Map<String, Map<String, Object>> publishPipeProps) {
if (Objects.nonNull(publishPipeProps)) {
return MapUtils.getString(publishPipeProps.get(publishPipeCode), KEY, StringUtils.SLASH);
}
return StringUtils.SLASH;
}
}

View File

@ -269,9 +269,7 @@ public class ContentServiceImpl implements IContentService {
CmsContent cmsContent = this.dao().getById(contentId);
Assert.notNull(cmsContent, () -> CommonErrorCode.DATA_NOT_FOUND_BY_ID.exception("contentId", contentId));
Long[] catalogIds = dto.getCatalogIds().stream().filter(id -> !Objects.equals(id, cmsContent.getCatalogId()))
.toArray(Long[]::new);
for (Long catalogId : catalogIds) {
for (Long catalogId : dto.getCatalogIds()) {
CmsCatalog catalog = this.catalogService.getCatalog(catalogId);
if (catalog == null) {
continue; // 目标栏目错误直接跳过

View File

@ -18,6 +18,7 @@ package com.chestnut.contentcore.service.impl;
import com.chestnut.common.staticize.StaticizeService;
import com.chestnut.common.staticize.core.TemplateContext;
import com.chestnut.contentcore.core.IDynamicPageType;
import com.chestnut.contentcore.core.impl.PublishPipeProp_ErrPageLink;
import com.chestnut.contentcore.domain.CmsSite;
import com.chestnut.contentcore.service.IPublishPipeService;
import com.chestnut.contentcore.service.ISiteService;
@ -61,8 +62,7 @@ public class DynamicPageService {
}
public void generateDynamicPage(String dynamicPageType, Long siteId, String publishPipeCode, Boolean preview,
Map<String, String> parameters, HttpServletResponse response)
throws IOException {
Map<String, String> parameters, HttpServletResponse response) throws IOException {
response.setCharacterEncoding(Charset.defaultCharset().displayName());
response.setContentType("text/html; charset=" + Charset.defaultCharset().displayName());
@ -71,11 +71,12 @@ public class DynamicPageService {
this.catchException("/", response, new RuntimeException("Site not found: " + siteId));
return;
}
String errPageLink = PublishPipeProp_ErrPageLink.getValue(publishPipeCode, site.getPublishPipeProps());
IDynamicPageType dpt = this.getDynamicPageType(dynamicPageType);
String template = this.publishPipeService.getPublishPipePropValue(dpt.getPublishPipeKey(), publishPipeCode, site.getPublishPipeProps());
File templateFile = this.templateService.findTemplateFile(site, template, publishPipeCode);
if (Objects.isNull(templateFile) || !templateFile.exists()) {
this.catchException(SiteUtils.getSiteLink(site, publishPipeCode, preview), response, new RuntimeException("Template not found: " + template));
this.catchException(errPageLink, response, new RuntimeException("Template not found: " + template));
return;
}
long s = System.currentTimeMillis();
@ -95,7 +96,7 @@ public class DynamicPageService {
this.staticizeService.process(templateContext, response.getWriter());
log.debug("动态模板解析,耗时:{} ms", System.currentTimeMillis() - s);
} catch (Exception e) {
this.catchException(SiteUtils.getSiteLink(site, publishPipeCode, preview), response, e);
this.catchException(errPageLink, response, e);
}
}
@ -103,7 +104,7 @@ public class DynamicPageService {
if (log.isDebugEnabled()) {
e.printStackTrace(response.getWriter());
} else {
response.sendRedirect(redirectLink); // TODO 通过发布通道属性配置错误页面
response.sendRedirect(redirectLink);
}
}
}

View File

@ -175,8 +175,6 @@ public class FileServiceImpl implements IFileService {
@Override
public void addFile(CmsSite site, FileAddDTO dto) throws IOException {
this.checkFileType(dto.getFileName());
String dir = dto.getDir();
dir = FileExUtils.normalizePath(dir);
if (dir.startsWith("/")) {

View File

@ -60,6 +60,8 @@ public class ResourceServiceImpl extends ServiceImpl<CmsResourceMapper, CmsResou
private final Map<String, IFileStorageType> fileStorageTypes;
private final List<IResourceStat> resourceStats;
private final ISiteService siteService;
@Override
@ -353,28 +355,13 @@ public class ResourceServiceImpl extends ServiceImpl<CmsResourceMapper, CmsResou
return sb.toString();
}
private final List<IResourceStat> resourceStats;
/**
* TODO 统计资源引用
* 统计资源引用
*/
public void statResourceUsage() {
Map<Long, Integer> resourceIds = new HashMap<>();
this.resourceStats.forEach(rs -> {
Map<Long, Integer> quotedResource = rs.findQuotedResource();
});
// 站点Logo栏目Logo内容Logo
// 页面部件区块Logo
// 页面部件广告图
// 文章内容
// 图集内容
// 音频内容
// 视频内容
public void statResourceUsage(Long siteId) throws InterruptedException {
Map<Long, Long> quotedResources = new HashMap<>();
for (IResourceStat resourceStat : this.resourceStats) {
resourceStat.statQuotedResource(siteId, quotedResources);
}
}
}

View File

@ -76,7 +76,6 @@ public class SiteThemeService {
private final List<ICoreDataHandler> contentCoreHandlers;
public AsyncTask importSiteTheme(CmsSite site, final File zipFile, LoginUser operator) {
// TODO 校验数据必须无栏目内容页面部件等数据的站点才能导入
AsyncTask asyncTask = new AsyncTask() {
@Override

View File

@ -182,7 +182,7 @@ public class CmsContentTag extends AbstractListTag {
return DESC;
}
enum LevelTagAttr {
public enum LevelTagAttr {
Root(ATTR_OPTION_LEVEL_ROOT),
Current(ATTR_OPTION_LEVEL_CURRENT),
Child(ATTR_OPTION_LEVEL_CHILD),

View File

@ -137,7 +137,7 @@ public class CmsIncludeTag extends AbstractTag {
Map<String, String> mergeParams = mergeRequestVariable(env, paramsMap);
env.setVariable(TemplateUtils.TemplateVariable_Request, wrap(env, mergeParams));
// TODO 兼容历史版本1.6.0移除IncludeRequest模板变量
env.setVariable("IncludeRequest", wrap(env, mergeParams));
env.setVariable(TemplateUtils.TemplateVariable_IncludeRequest, wrap(env, mergeParams));
env.include(includeTemplate);
} else if (virtual) {
// 动态模板
@ -198,7 +198,7 @@ public class CmsIncludeTag extends AbstractTag {
Map<String, String> mergeParams = mergeRequestVariable(env, params);
env.setVariable(TemplateUtils.TemplateVariable_Request, wrap(env, mergeParams));
// TODO 兼容历史版本1.6.0版本移除IncludeRequest模板变量
env.setVariable("IncludeRequest", wrap(env, mergeParams));
env.setVariable(TemplateUtils.TemplateVariable_IncludeRequest, wrap(env, mergeParams));
env.include(includeTemplate);
return writer.getBuffer().toString();
} finally {

View File

@ -44,8 +44,9 @@ public class TemplateUtils {
public final static String TemplateVariable_Request = "Request";
/**
* 模板变量<@cms_include>标签file属性请求参数
* 模板变量<@cms_include>标签file属性请求参数下个大版本移除
*/
@Deprecated(forRemoval = true)
public final static String TemplateVariable_IncludeRequest = "IncludeRequest";
/**
@ -113,6 +114,11 @@ public class TemplateUtils {
*/
public final static String TemplateVariable_OBJ_Link = "link";
/**
* 模板变量客户端类型适用动态模板访问
*/
public final static String TemplateVariable_ClientType = "ClientType";
public static Long evalSiteId(Environment env) throws TemplateModelException {
return FreeMarkerUtils.evalLongVariable(env, "Site.siteId");
}

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-cms</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<artifactId>chestnut-cms-customform</artifactId>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-cms</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<artifactId>chestnut-cms-dynamic</artifactId>

View File

@ -18,7 +18,6 @@ package com.chestnut.cms.dynamic.controller.front;
import com.chestnut.cms.dynamic.service.IDynamicPageService;
import com.chestnut.common.security.web.BaseRestController;
import com.chestnut.common.utils.ServletUtils;
import com.chestnut.contentcore.service.ISiteService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
@ -26,7 +25,6 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@ -44,8 +42,6 @@ import java.util.Map;
@RequestMapping("/dynamic/page")
public class DynamicPageFrontController extends BaseRestController {
private final ISiteService siteService;
private final IDynamicPageService dynamicPageService;
@GetMapping

View File

@ -38,7 +38,6 @@ public class DynamicListener {
CmsSite site = event.getSite();
int pageSize = 500;
try {
// 删除友链数据
long total = this.dynamicPageService
.count(new LambdaQueryWrapper<CmsDynamicPage>().eq(CmsDynamicPage::getSiteId, site.getSiteId()));
for (long i = 0; i * pageSize < total; i++) {

View File

@ -29,6 +29,7 @@ import com.chestnut.common.staticize.core.TemplateContext;
import com.chestnut.common.utils.Assert;
import com.chestnut.common.utils.IdUtils;
import com.chestnut.common.utils.SpringUtils;
import com.chestnut.contentcore.core.impl.PublishPipeProp_ErrPageLink;
import com.chestnut.contentcore.domain.CmsSite;
import com.chestnut.contentcore.service.ISiteService;
import com.chestnut.contentcore.service.ITemplateService;
@ -182,17 +183,16 @@ public class DynamicPageServiceImpl extends ServiceImpl<CmsDynamicPageMapper, Cm
this.catchException("/", response, new RuntimeException("Site not found: " + siteId));
return;
}
String errPageLink = PublishPipeProp_ErrPageLink.getValue(publishPipeCode, site.getPublishPipeProps());
CmsDynamicPage dynamicPage = dynamicPageHelper.getDynamicPageByPath(siteId, requestURI);
String template = dynamicPage.getTemplates().get(publishPipeCode);
File templateFile = this.templateService.findTemplateFile(site, template, publishPipeCode);
if (Objects.isNull(templateFile) || !templateFile.exists()) {
this.catchException(SiteUtils.getSiteLink(site, publishPipeCode, preview), response, new RuntimeException("Template not found: " + template));
if (Objects.isNull(templateFile)) {
this.catchException(errPageLink, response, new RuntimeException("Template not found: " + template));
return;
}
long s = System.currentTimeMillis();
try {
// TODO 校验输入参数
// 生成静态页面
// 模板ID = 通道:站点目录:模板文件名
String templateKey = SiteUtils.getTemplateKey(site, publishPipeCode, template);
@ -203,18 +203,17 @@ public class DynamicPageServiceImpl extends ServiceImpl<CmsDynamicPageMapper, Cm
templateContext.getVariables().put(TemplateUtils.TemplateVariable_Request, parameters);
// 动态页面自定义数据
if (Objects.nonNull(dynamicPage.getInitDataTypes())) {
dynamicPage.getInitDataTypes().forEach(initDataType -> {
for (String initDataType : dynamicPage.getInitDataTypes()) {
IDynamicPageInitData initData = dynamicPageHelper.getDynamicPageInitData(initDataType);
if (Objects.nonNull(initData)) {
initData.initTemplateData(templateContext, parameters);
}
});
}
}
// staticize
this.staticizeService.process(templateContext, response.getWriter());
log.debug("动态模板解析,耗时:{} ms", System.currentTimeMillis() - s);
} catch (Exception e) {
this.catchException(SiteUtils.getSiteLink(site, publishPipeCode, preview), response, e);
this.catchException(errPageLink, response, e);
}
}
@ -222,7 +221,7 @@ public class DynamicPageServiceImpl extends ServiceImpl<CmsDynamicPageMapper, Cm
if (log.isDebugEnabled()) {
e.printStackTrace(response.getWriter());
} else {
response.sendRedirect(redirectLink); // TODO 通过发布通道属性配置错误页面
response.sendRedirect(redirectLink);
}
}
}

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-cms</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<artifactId>chestnut-cms-exmodel</artifactId>

View File

@ -83,9 +83,6 @@ public class ExModelService {
fv = f.getDefaultValue();
}
XModelFieldDataDTO dto = XModelFieldDataDTO.newInstance(f, fv);
IMetaControlType controlType = controlTypeMap.get(IMetaControlType.BEAN_PREFIX + f.getControlType());
Object objectV = controlType.stringAsValue(Objects.isNull(dto.getValue()) ? "" : dto.getValue().toString());
dto.setValue(objectV);
list.add(dto);
});
return list;

View File

@ -21,11 +21,9 @@ import com.chestnut.common.staticize.exception.InvalidTagAttrValueException;
import com.chestnut.common.staticize.tag.AbstractTag;
import com.chestnut.common.staticize.tag.TagAttr;
import com.chestnut.common.staticize.tag.TagAttrOption;
import com.chestnut.common.utils.ConvertUtils;
import com.chestnut.common.utils.IdUtils;
import com.chestnut.exmodel.CmsExtendMetaModelType;
import com.chestnut.xmodel.core.IMetaControlType;
import com.chestnut.xmodel.core.MetaModel;
import com.chestnut.xmodel.service.IModelDataService;
import com.chestnut.xmodel.service.IModelService;
import freemarker.core.Environment;
@ -85,17 +83,6 @@ public class CmsXModelDataTag extends AbstractTag {
CmsExtendMetaModelType.FIELD_DATA_TYPE.getCode(), dataType,
CmsExtendMetaModelType.FIELD_DATA_ID.getCode(), dataId
));
MetaModel model = this.modelService.getMetaModel(modelId);
modelData.entrySet().forEach(entry -> {
model.getFields().stream().filter(field -> field.getCode().equals(entry.getKey()))
.findFirst().ifPresent(field -> {
IMetaControlType controlType = controlTypeMap.get(IMetaControlType.BEAN_PREFIX + field.getControlType());
if (controlType != null) {
Object v = controlType.stringAsValue(ConvertUtils.toStr(entry.getValue()));
entry.setValue(v);
}
});
});
return Map.of(StaticizeConstants.TemplateVariable_Data, this.wrap(env, modelData));
}

View File

@ -17,7 +17,6 @@ package com.chestnut.cms.exmodel;
import com.chestnut.common.utils.StringUtils;
import com.chestnut.xmodel.core.BaseModelData;
import jakarta.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
@ -26,7 +25,7 @@ import java.util.List;
import java.util.stream.Collectors;
/**
* <TODO description class purpose>
* 生成元数据模型类通用方法
*
* @author 兮玥
* @email 190785909@qq.com

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-cms</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<artifactId>chestnut-cms-image</artifactId>

View File

@ -0,0 +1,81 @@
/*
* 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.cms.image;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chestnut.cms.image.domain.CmsImage;
import com.chestnut.cms.image.service.IImageService;
import com.chestnut.common.async.AsyncTaskManager;
import com.chestnut.contentcore.core.IResourceStat;
import com.chestnut.contentcore.core.InternalURL;
import com.chestnut.contentcore.util.InternalUrlUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 图集内容资源引用统计
*
* @author 兮玥
* @email 190785909@qq.com
*/
@RequiredArgsConstructor
@Component(IResourceStat.BEAN_PREFIX + ImageContentResourceStat.TYPE)
public class ImageContentResourceStat implements IResourceStat {
public static final String TYPE = "ImageContent";
private final IImageService imageService;
@Override
public String getType() {
return TYPE;
}
@Override
public void statQuotedResource(Long siteId, Map<Long, Long> quotedResources) throws InterruptedException {
int pageSize = 1000;
long lastId = 0L;
int count = 0;
long total = imageService.dao().lambdaQuery().select(List.of()).eq(CmsImage::getSiteId, siteId).count();
while (true) {
LambdaQueryWrapper<CmsImage> q = new LambdaQueryWrapper<CmsImage>()
.select(List.of(CmsImage::getPath))
.eq(CmsImage::getSiteId, siteId)
.gt(CmsImage::getImageId, lastId)
.orderByAsc(CmsImage::getImageId);
Page<CmsImage> page = imageService.dao().page(new Page<>(0, pageSize, false), q);
for (CmsImage image : page.getRecords()) {
AsyncTaskManager.setTaskProgressInfo((int) (count * 100 / total),
"正在统计图集内容资源引用:" + count + " / " + total + "]");
lastId = image.getImageId();
InternalURL internalURL = InternalUrlUtils.parseInternalUrl(image.getPath());
if (Objects.nonNull(internalURL)) {
quotedResources.compute(internalURL.getId(), (k, v) -> Objects.isNull(v) ? 1 : v + 1);
}
AsyncTaskManager.checkInterrupt();
count++;
}
if (page.getRecords().size() < pageSize) {
break;
}
}
}
}

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-cms</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<artifactId>chestnut-cms-link</artifactId>

View File

@ -0,0 +1,81 @@
/*
* 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.link;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chestnut.common.async.AsyncTaskManager;
import com.chestnut.contentcore.core.IResourceStat;
import com.chestnut.contentcore.core.InternalURL;
import com.chestnut.contentcore.util.InternalUrlUtils;
import com.chestnut.link.domain.CmsLink;
import com.chestnut.link.service.ILinkService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 图集内容资源引用统计
*
* @author 兮玥
* @email 190785909@qq.com
*/
@RequiredArgsConstructor
@Component(IResourceStat.BEAN_PREFIX + FriendLinkResourceStat.TYPE)
public class FriendLinkResourceStat implements IResourceStat {
public static final String TYPE = "FriendLink";
private final ILinkService linkService;
@Override
public String getType() {
return TYPE;
}
@Override
public void statQuotedResource(Long siteId, Map<Long, Long> quotedResources) throws InterruptedException {
int pageSize = 1000;
long lastId = 0L;
int count = 0;
long total = linkService.lambdaQuery().select(List.of()).eq(CmsLink::getSiteId, siteId).count();
while (true) {
LambdaQueryWrapper<CmsLink> q = new LambdaQueryWrapper<CmsLink>()
.select(List.of(CmsLink::getLogo))
.eq(CmsLink::getSiteId, siteId)
.gt(CmsLink::getLinkId, lastId)
.orderByAsc(CmsLink::getLinkId);
Page<CmsLink> page = linkService.page(new Page<>(0, pageSize, false), q);
for (CmsLink friendLink : page.getRecords()) {
AsyncTaskManager.setTaskProgressInfo((int) (count * 100 / total),
"正在统计友情链接资源引用:" + count + " / " + total + "]");
lastId = friendLink.getLinkId();
InternalURL internalURL = InternalUrlUtils.parseInternalUrl(friendLink.getLogo());
if (Objects.nonNull(internalURL)) {
quotedResources.compute(internalURL.getId(), (k, v) -> Objects.isNull(v) ? 1 : v + 1);
}
AsyncTaskManager.checkInterrupt();
count++;
}
if (page.getRecords().size() < pageSize) {
break;
}
}
}
}

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-cms</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<artifactId>chestnut-cms-media</artifactId>

View File

@ -0,0 +1,128 @@
/*
* 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.media;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chestnut.common.async.AsyncTaskManager;
import com.chestnut.contentcore.core.IResourceStat;
import com.chestnut.contentcore.core.InternalURL;
import com.chestnut.contentcore.util.InternalUrlUtils;
import com.chestnut.media.dao.CmsAudioDAO;
import com.chestnut.media.dao.CmsVideoDAO;
import com.chestnut.media.domain.CmsAudio;
import com.chestnut.media.domain.CmsVideo;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 音视频内容资源引用统计
*
* @author 兮玥
* @email 190785909@qq.com
*/
@RequiredArgsConstructor
@Component(IResourceStat.BEAN_PREFIX + MediaResourceStat.TYPE)
public class MediaResourceStat implements IResourceStat {
public static final String TYPE = "Media";
private final CmsVideoDAO videoDao;
private final CmsAudioDAO audioDAO;
@Override
public String getType() {
return TYPE;
}
@Override
public void statQuotedResource(Long siteId, Map<Long, Long> quotedResources) throws InterruptedException {
statAudioResource(siteId, quotedResources);
statVideoResource(siteId, quotedResources);
}
private void statAudioResource(Long siteId, Map<Long, Long> quotedResources) throws InterruptedException {
int pageSize = 1000;
long lastId = 0L;
int count = 0;
long total = audioDAO.lambdaQuery().eq(CmsAudio::getSiteId, siteId).count();
while (true) {
LambdaQueryWrapper<CmsAudio> q = new LambdaQueryWrapper<CmsAudio>()
.select(List.of(CmsAudio::getPath))
.eq(CmsAudio::getSiteId, siteId)
.gt(CmsAudio::getAudioId, lastId)
.orderByAsc(CmsAudio::getAudioId);
Page<CmsAudio> page = audioDAO.page(new Page<>(0, pageSize, false), q);
for (CmsAudio audio : page.getRecords()) {
AsyncTaskManager.setTaskProgressInfo((int) (count * 100 / total),
"正在统计音频资源引用:" + count + " / " + total + "]");
lastId = audio.getAudioId();
// 音频
InternalURL internalURL = InternalUrlUtils.parseInternalUrl(audio.getPath());
if (Objects.nonNull(internalURL)) {
quotedResources.compute(internalURL.getId(), (k, v) -> Objects.isNull(v) ? 1 : v + 1);
}
AsyncTaskManager.checkInterrupt();
count++;
}
if (page.getRecords().size() < pageSize) {
break;
}
}
}
private void statVideoResource(Long siteId, Map<Long, Long> quotedResources) throws InterruptedException {
int pageSize = 1000;
long lastId = 0L;
int count = 0;
long total = videoDao.lambdaQuery().eq(CmsVideo::getSiteId, siteId).count();
while (true) {
LambdaQueryWrapper<CmsVideo> q = new LambdaQueryWrapper<CmsVideo>()
.select(List.of(CmsVideo::getPath, CmsVideo::getCover))
.eq(CmsVideo::getSiteId, siteId)
.gt(CmsVideo::getVideoId, lastId)
.orderByAsc(CmsVideo::getVideoId);
Page<CmsVideo> page = videoDao.page(new Page<>(0, pageSize, false), q);
for (CmsVideo video : page.getRecords()) {
AsyncTaskManager.setTaskProgressInfo((int) (count * 100 / total),
"正在统计视频资源引用:" + count + " / " + total + "]");
lastId = video.getVideoId();
// 视频封面
InternalURL internalURL = InternalUrlUtils.parseInternalUrl(video.getCover());
if (Objects.nonNull(internalURL)) {
quotedResources.compute(internalURL.getId(), (k, v) -> Objects.isNull(v) ? 1 : v + 1);
}
// 视频
if (!VideoContent.TYPE_SHARE.equals(video.getType())) {
internalURL = InternalUrlUtils.parseInternalUrl(video.getPath());
if (Objects.nonNull(internalURL)) {
quotedResources.compute(internalURL.getId(), (k, v) -> Objects.isNull(v) ? 1 : v + 1);
}
}
AsyncTaskManager.checkInterrupt();
count++;
}
if (page.getRecords().size() < pageSize) {
break;
}
}
}
}

View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-cms</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<artifactId>chestnut-cms-member</artifactId>

View File

@ -21,7 +21,7 @@ import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* <TODO description class purpose>
* 会员内容发布信息VO
*
* @author 兮玥
* @email 190785909@qq.com

View File

@ -23,7 +23,7 @@ import freemarker.core.Environment;
import freemarker.template.TemplateModelException;
/**
* <TODO description class purpose>
* CMS会员工具类
*
* @author 兮玥
* @email 190785909@qq.com
@ -35,7 +35,7 @@ public class CmsMemberUtils {
Long siteId = TemplateUtils.evalSiteId(env);
TemplateContext context = FreeMarkerUtils.getTemplateContext(env);
if (context.isPreview()) {
url = FreeMarkerUtils.evalStringVariable(env, "ApiPrefix")
url = FreeMarkerUtils.evalStringVariable(env, TemplateUtils.TemplateVariable_ApiPrefix)
+ "account/" + memberId + "?sid=" + siteId + "&pp=" + context.getPublishPipeCode() + "&preview=true";
} else {
url = FreeMarkerUtils.evalStringVariable(env, "Prefix") + "account/" + memberId;

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-cms</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<artifactId>chestnut-cms-search</artifactId>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-cms</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<artifactId>chestnut-cms-seo</artifactId>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-cms</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<artifactId>chestnut-cms-stat</artifactId>

View File

@ -1,35 +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.cms.stat.domain.vo;
import lombok.Getter;
import lombok.Setter;
/**
* <TODO description class purpose>
*
* @author 兮玥
* @email 190785909@qq.com
*/
@Getter
@Setter
public class ContentStatusTotal {
private String status;
private Integer total;
}

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-cms</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<artifactId>chestnut-cms-vote</artifactId>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-cms</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<artifactId>chestnut-cms-word</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>chestnut-common</artifactId>
<groupId>com.chestnut</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -19,19 +19,18 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.StringUtils;
import java.util.*;
/**
* <TODO description class purpose>
* 国际化字符串占位符处理器
*
* @author 兮玥
* @email 190785909@qq.com
*/
public class I18nPlaceholderHelper {
private static final Log logger = LogFactory.getLog(PropertyPlaceholderHelper.class);
private static final Log logger = LogFactory.getLog(I18nPlaceholderHelper.class);
private static final Map<String, String> wellKnownSimplePrefixes = new HashMap<>(4);
private final String placeholderPrefix;
private final String placeholderSuffix;

View File

@ -112,4 +112,11 @@ public class ArrayUtils {
});
return map;
}
public static <T, R> List<R> mapNotNull(List<T> list, Function<T, R> mapper) {
if (Objects.isNull(list)) {
return List.of();
}
return list.stream().map(mapper).filter(Objects::nonNull).toList();
}
}

View File

@ -66,4 +66,8 @@ public class ObjectUtils {
public static <T, R> R ifNullOrElse(T obj, Supplier<R> nullSupplier, Function<T, R> nonNullFunc) {
return Objects.isNull(obj) ? nullSupplier.get() : nonNullFunc.apply(obj);
}
public static <T> String nonNullOrElseAsString(T obj, Function<T, String> nonNullFunc) {
return Objects.isNull(obj) ? StringUtils.EMPTY : nonNullFunc.apply(obj);
}
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>chestnut-common</artifactId>
<groupId>com.chestnut</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>chestnut-common</artifactId>
<groupId>com.chestnut</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>chestnut-common</artifactId>
<groupId>com.chestnut</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>chestnut-common</artifactId>
<groupId>com.chestnut</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>chestnut-common</artifactId>
<groupId>com.chestnut</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>chestnut-common</artifactId>
<groupId>com.chestnut</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>chestnut-common</artifactId>
<groupId>com.chestnut</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -3,7 +3,7 @@
<parent>
<artifactId>chestnut-modules</artifactId>
<groupId>com.chestnut</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -16,5 +16,10 @@
<groupId>com.chestnut</groupId>
<artifactId>chestnut-member</artifactId>
</dependency>
<!-- 词汇模块 -->
<dependency>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-word</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -40,6 +40,7 @@ import com.chestnut.common.utils.ServletUtils;
import com.chestnut.member.domain.vo.MemberCache;
import com.chestnut.member.service.IMemberExpConfigService;
import com.chestnut.member.service.IMemberStatDataService;
import com.chestnut.word.service.ISensitiveWordService;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.redisson.api.RLock;
@ -72,6 +73,8 @@ public class CommentApiServiceImpl implements ICommentApiService, ApplicationCon
private final IMemberExpConfigService memberExpConfigService;
private final ISensitiveWordService sensitiveWordService;
private ApplicationContext applicationContext;
@Override
@ -144,7 +147,9 @@ public class CommentApiServiceImpl implements ICommentApiService, ApplicationCon
comment.setSourceType(dto.getSourceType());
comment.setSourceId(dto.getSourceId());
comment.setUid(dto.getOperator().getUserId());
comment.setContent(dto.getContent()); // TODO 敏感词过滤
// 敏感词过滤
String content = sensitiveWordService.replaceSensitiveWords(dto.getContent(), null);
comment.setContent(content);
comment.setCommentTime(LocalDateTime.now());
comment.setAuditStatus(CommentAuditStatus.TO_AUDIT);
comment.setLikeCount(0);
@ -160,7 +165,7 @@ public class CommentApiServiceImpl implements ICommentApiService, ApplicationCon
if (!comment.getSourceType().equals(parent.getSourceType())
|| !comment.getSourceId().equals(parent.getSourceId())
|| parent.getParentId() != 0) {
throw new RuntimeException("评论数据源异常!");
throw new RuntimeException("Reply comment not found: " + dto.getCommentId());
}
comment.setReplyUid(dto.getReplyUid());
comment.setParentId(dto.getCommentId());

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>chestnut-modules</artifactId>
<groupId>com.chestnut</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -3,7 +3,7 @@
<parent>
<artifactId>chestnut-modules</artifactId>
<groupId>com.chestnut</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -20,7 +20,7 @@ import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* <TODO description class purpose>
* 会员配置
*
* @author 兮玥
* @email 190785909@qq.com

View File

@ -16,7 +16,7 @@
package com.chestnut.member.core;
/**
* <TODO description class purpose>
* 会员注册方式
*
* @author 兮玥
* @email 190785909@qq.com

View File

@ -19,7 +19,7 @@ import lombok.Getter;
import lombok.Setter;
/**
* <TODO description class purpose>
* 收藏DTO
*
* @author 兮玥
* @email 190785909@qq.com

View File

@ -21,7 +21,7 @@ import lombok.Getter;
import lombok.Setter;
/**
* <TODO description class purpose>
* 点赞DTO
*
* @author 兮玥
* @email 190785909@qq.com

View File

@ -18,10 +18,9 @@ package com.chestnut.member.domain.dto;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
import org.springframework.validation.annotation.Validated;
/**
* <TODO description class purpose>
* 会员信息修改DTO
*
* @author 兮玥
* @email 190785909@qq.com

View File

@ -20,7 +20,7 @@ import lombok.Getter;
import lombok.Setter;
/**
* <TODO description class purpose>
* 会员头像上传DTO
*
* @author 兮玥
* @email 190785909@qq.com

View File

@ -20,13 +20,18 @@ import com.chestnut.member.fixed.config.MemberResourcePrefix;
import com.chestnut.system.fixed.config.BackendContext;
/**
* <TODO description class purpose>
* 会员工具类
*
* @author 兮玥
* @email 190785909@qq.com
*/
public class MemberUtils {
/**
* 获取会员资源文件访问前缀
*
* @param isPreview 是否预览模式
*/
public static String getMemberResourcePrefix(boolean isPreview) {
if (isPreview) {
return BackendContext.getValue() + MemberConfig.getResourcePrefix();

View File

@ -6,7 +6,7 @@
<parent>
<artifactId>chestnut-modules</artifactId>
<groupId>com.chestnut</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -18,7 +18,6 @@ package com.chestnut.xmodel.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.chestnut.common.db.util.SqlBuilder;
import com.chestnut.common.utils.Assert;
import com.chestnut.common.utils.JacksonUtils;
import com.chestnut.common.utils.ObjectUtils;
import com.chestnut.common.utils.StringUtils;
import com.chestnut.xmodel.core.*;
@ -31,7 +30,10 @@ import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.MapUtils;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@ -213,11 +215,15 @@ public class ModelDataServiceImpl implements IModelDataService {
Map<String, Object> dataMap = new HashMap<>();
// 固定字段
mmt.getFixedFields().forEach(f -> {
dataMap.put(f.getCode(), map.get(f.getFieldName()));
Object v = map.get(f.getFieldName());
dataMap.put(f.getCode(), v);
});
// 自定义字段
model.getFields().forEach(f -> {
dataMap.put(f.getCode(), map.get(f.getFieldName()));
Object v = map.get(f.getFieldName());
IMetaControlType controlType = getControlType(f.getControlType());
Object objectV = controlType.stringAsValue(ObjectUtils.nonNullOrElseAsString(v, Object::toString));
dataMap.put(f.getCode(), objectV);
});
return dataMap;
}

View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.chestnut</groupId>
<artifactId>chestnut-modules</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<artifactId>chestnut-monitor</artifactId>

View File

@ -3,7 +3,7 @@
<parent>
<artifactId>chestnut-modules</artifactId>
<groupId>com.chestnut</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -6,7 +6,7 @@
<parent>
<artifactId>chestnut-modules</artifactId>
<groupId>com.chestnut</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -20,7 +20,7 @@ package com.chestnut.stat.core;
*/
public interface IStatEventHandler {
static final String BEAN_PREFIX = "StatEventHandler_";
String BEAN_PREFIX = "StatEventHandler_";
/**
* 事件类型唯一标识

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>chestnut-modules</artifactId>
<groupId>com.chestnut</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -1,7 +1,8 @@
# 校验消息
VALIDATOR.SYSTEM.INVALID_DICT_VALUE=字典【{0}】数据非法:{1}
VALIDATOR.SYSTEM.USER_GENDER=用户性别输入错误:{1}
VALIDATOR.SYSTEM.USER_GENDER=用户性别输入错误:{0}
VALIDATOR.SYSTEM.INVALID_LONG_ID=长整形ID参数值错误{0}
VALIDATOR.SYSTEM.SCRIPT_TEXT=Groovy脚本不能为空
#错误消息
ERRCODE.SYS.UNAME_PWD_REQUIRED=账号/密码不能为空

View File

@ -1,6 +1,7 @@
VALIDATOR.SYSTEM.INVALID_DICT_VALUE=Invalid dict value "{1}" for [{0}]
VALIDATOR.SYSTEM.USER_GENDER=Invalid user gender value: {1}
VALIDATOR.SYSTEM.USER_GENDER=Invalid user gender value: {0}
VALIDATOR.SYSTEM.INVALID_LONG_ID=Invalid long id value: {0}
VALIDATOR.SYSTEM.SCRIPT_TEXT=Groovy script cannot be empty.
#错误消息
ERRCODE.SYS.UNAME_PWD_REQUIRED=Account/Password cannot be empty.

View File

@ -1,7 +1,8 @@
# 校驗消息
VALIDATOR.SYSTEM.INVALID_DICT_VALUE=字典【{0}】數據非法:{1}
VALIDATOR.SYSTEM.USER_GENDER=用戶性別輸入錯誤:{1}
VALIDATOR.SYSTEM.USER_GENDER=用戶性別輸入錯誤:{0}
VALIDATOR.SYSTEM.INVALID_LONG_ID=長整形ID參數值錯誤{0}
VALIDATOR.SYSTEM.SCRIPT_TEXT=Groovy腳本不能為空
#錯誤消息
ERRCODE.SYS.UNAME_PWD_REQUIRED=賬號/密碼不能為空

View File

@ -3,7 +3,7 @@
<parent>
<artifactId>chestnut-modules</artifactId>
<groupId>com.chestnut</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -6,7 +6,7 @@
<parent>
<artifactId>chestnut-modules</artifactId>
<groupId>com.chestnut</groupId>
<version>1.5.2</version>
<version>1.5.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -27,7 +27,7 @@ public interface ISensitiveWordService extends IService<SensitiveWord> {
* 替换敏感词
*
* @param text
* @param replaceStr
* @param replacement
* @return
*/
String replaceSensitiveWords(String text, String replacement);

Some files were not shown because too many files have changed in this diff Show More