更新版本1.3.24

This commit is contained in:
兮玥 2023-12-24 16:21:44 +08:00
parent 4d84c3de19
commit 7c4f76f7b9
97 changed files with 1929 additions and 962 deletions

View File

@ -1,4 +1,4 @@
# ChestnutCMS v1.3.23
# ChestnutCMS v1.3.24
### 系统简介

View File

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

View File

@ -3,7 +3,7 @@ chestnut:
# 名称
name: ChestnutCMS
# 版本
version: 1.3.23
version: 1.3.24
# 版权年份
copyrightYear: 2023
system:
@ -55,7 +55,7 @@ spring:
servlet:
multipart:
# 单个文件大小
max-file-size: 20MB
max-file-size: 50MB
# 设置总上传的文件大小
max-request-size: 100MB
# 服务模块

View File

@ -3,7 +3,7 @@ chestnut:
# 名称
name: ChestnutCMS
# 版本
version: 1.3.23
version: 1.3.24
# 版权年份
copyrightYear: 2023
system:
@ -54,7 +54,7 @@ spring:
servlet:
multipart:
# 单个文件大小
max-file-size: 20MB
max-file-size: 50MB
# 设置总上传的文件大小
max-request-size: 100MB
# 服务模块

View File

@ -3,7 +3,7 @@ chestnut:
# 名称
name: ChestnutCMS
# 版本
version: 1.3.23
version: 1.3.24
# 版权年份
copyrightYear: 2023
system:

View File

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

View File

@ -0,0 +1,66 @@
package com.chestnut.advertisement;
import com.chestnut.advertisement.domain.CmsAdvertisement;
import com.chestnut.advertisement.service.IAdvertisementService;
import com.chestnut.common.async.AsyncTaskManager;
import com.chestnut.common.utils.IdUtils;
import com.chestnut.common.utils.JacksonUtils;
import com.chestnut.contentcore.core.ICoreDataHandler;
import com.chestnut.contentcore.core.SiteExportContext;
import com.chestnut.contentcore.core.SiteImportContext;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.List;
/**
* 广告页面部件内容核心数据处理器
*
* @author 兮玥
* @email 190785909@qq.com
*/
@Component
@RequiredArgsConstructor
public class AdCoreDataHandler implements ICoreDataHandler {
private final IAdvertisementService advertisementService;
@Override
public void onSiteExport(SiteExportContext context) {
// cms_advertisement
int percent = AsyncTaskManager.getTaskProgressPercent();
AsyncTaskManager.setTaskProgressInfo( percent + (100 - percent) / 10,
"正在导出广告数据");
List<CmsAdvertisement> list = advertisementService.lambdaQuery()
.eq(CmsAdvertisement::getSiteId, context.getSite().getSiteId())
.list();
context.saveData(CmsAdvertisement.TABLE_NAME, JacksonUtils.to(list));
}
@Override
public void onSiteImport(SiteImportContext context) {
int percent = AsyncTaskManager.getTaskProgressPercent();
AsyncTaskManager.setTaskProgressInfo( percent + (100 - percent) / 10,
"正在导入广告数据");
List<File> files = context.readDataFiles(CmsAdvertisement.TABLE_NAME);
files.forEach(f -> {
List<CmsAdvertisement> list = JacksonUtils.fromList(f, CmsAdvertisement.class);
for (CmsAdvertisement data : list) {
try {
data.setAdvertisementId(IdUtils.getSnowflakeId());
data.setSiteId(context.getSite().getSiteId());
data.setAdSpaceId(context.getPageWidgetIdMap().get(data.getAdSpaceId()));
data.createBy(context.getOperator());
data.setResourcePath(context.dealInternalUrl(data.getResourcePath()));
data.setRedirectUrl(context.dealInternalUrl(data.getRedirectUrl()));
advertisementService.save(data);
} catch (Exception e) {
AsyncTaskManager.addErrMessage("导入广告数据失败:" + data.getName()
+ "[" + data.getAdvertisementId() + "]");
e.printStackTrace();
}
}
});
}
}

View File

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

View File

@ -0,0 +1,112 @@
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.service.IArticleService;
import com.chestnut.common.async.AsyncTaskManager;
import com.chestnut.common.utils.JacksonUtils;
import com.chestnut.common.utils.StringUtils;
import com.chestnut.contentcore.core.ICoreDataHandler;
import com.chestnut.contentcore.core.InternalURL;
import com.chestnut.contentcore.core.SiteExportContext;
import com.chestnut.contentcore.core.SiteImportContext;
import com.chestnut.contentcore.util.InternalUrlUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
/**
* 文章内容核心数据处理器
*
* @author 兮玥
* @email 190785909@qq.com
*/
@Component
@RequiredArgsConstructor
public class ArticleCoreDataHandler implements ICoreDataHandler {
private final IArticleService articleService;
@Override
public void onSiteExport(SiteExportContext context) {
// cms_article_detail
int percent = AsyncTaskManager.getTaskProgressPercent();
AsyncTaskManager.setTaskProgressInfo( percent + (100 - percent) / 10,
"正在导出文章详情数据");
long offset = 0;
int pageSize = 200;
int fileIndex = 1;
while (true) {
LambdaQueryWrapper<CmsArticleDetail> q = new LambdaQueryWrapper<CmsArticleDetail>()
.eq(CmsArticleDetail::getSiteId, context.getSite().getSiteId())
.gt(CmsArticleDetail::getContentId, offset)
.orderByAsc(CmsArticleDetail::getContentId);
Page<CmsArticleDetail> page = articleService.page(new Page<>(1, pageSize, false), q);
if (page.getRecords().size() > 0) {
context.saveData(CmsArticleDetail.TABLE_NAME, JacksonUtils.to(page.getRecords()), fileIndex);
if (page.getRecords().size() < pageSize) {
break;
}
offset = page.getRecords().get(page.getRecords().size() - 1).getContentId();
fileIndex++;
} else {
break;
}
}
}
@Override
public void onSiteImport(SiteImportContext context) {
int percent = AsyncTaskManager.getTaskProgressPercent();
AsyncTaskManager.setTaskProgressInfo( percent + (100 - percent) / 10,
"正在导入文章详情数据");
// cms_article_detail
List<File> files = context.readDataFiles(CmsArticleDetail.TABLE_NAME);
files.forEach(file -> {
List<CmsArticleDetail> list = JacksonUtils.fromList(file, CmsArticleDetail.class);
for (CmsArticleDetail data : list) {
Long oldContentId = data.getContentId();
try {
Long contentId = context.getContentIdMap().get(oldContentId);
data.setContentId(contentId);
data.setSiteId(context.getSite().getSiteId());
String contentHtml = data.getContentHtml();
// 替换正文内部资源地址
StringBuilder html = new StringBuilder();
int index = 0;
Matcher matcher = InternalUrlUtils.InternalUrlTagPattern.matcher(data.getContentHtml());
while (matcher.find()) {
String tagStr = matcher.group();
String iurl = matcher.group(1);
// begin
try {
InternalURL internalUrl = InternalUrlUtils.parseInternalUrl(iurl);
if (Objects.nonNull(internalUrl)) {
Long resourceId = context.getResourceIdMap().get(internalUrl.getId());
internalUrl.setId(resourceId);
tagStr = StringUtils.replaceEx(tagStr, iurl, internalUrl.toIUrl());
}
} catch (Exception e) {
e.printStackTrace();
}
// end
html.append(contentHtml, index, matcher.start()).append(tagStr);
index = matcher.end();
}
html.append(contentHtml.substring(index));
data.setContentHtml(html.toString());
articleService.save(data);
} catch (Exception e) {
AsyncTaskManager.addErrMessage("导入文章数据失败:" + oldContentId);
e.printStackTrace();
}
}
});
}
}

View File

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

View File

@ -0,0 +1,52 @@
package com.chestnut.block;
import com.chestnut.common.async.AsyncTaskManager;
import com.chestnut.common.utils.JacksonUtils;
import com.chestnut.common.utils.StringUtils;
import com.chestnut.contentcore.core.ICoreDataHandler;
import com.chestnut.contentcore.core.SiteImportContext;
import com.chestnut.contentcore.domain.CmsPageWidget;
import com.chestnut.contentcore.service.IPageWidgetService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Objects;
/**
* 自定义区块页面部件内容核心数据处理器
*
* @author 兮玥
* @email 190785909@qq.com
*/
@Component
@RequiredArgsConstructor
public class BlockCoreDataHandler implements ICoreDataHandler {
private final IPageWidgetService pageWidgetService;
@Override
public void onSiteImport(SiteImportContext context) {
int percent = AsyncTaskManager.getTaskProgressPercent();
AsyncTaskManager.setTaskProgressInfo( percent + (100 - percent) / 10,
"正在处理自定义区块详情数据");
this.pageWidgetService.lambdaQuery()
.eq(CmsPageWidget::getSiteId, context.getSite().getSiteId())
.eq(CmsPageWidget::getType, ManualPageWidgetType.ID)
.list().forEach(pageWidget -> {
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 -> {
// 处理iurl
item.setLogo(context.dealInternalUrl(item.getLogo()));
item.setUrl(context.dealInternalUrl(item.getUrl()));
});
});
}
}
});
}
}

View File

@ -19,17 +19,17 @@ public class ManualPageWidget extends AbstractPageWidget {
@Override
public void add() {
this.dealContentIngoreFields();
this.dealContentIgnoreFields();
super.add();
}
@Override
public void save() {
this.dealContentIngoreFields();
this.dealContentIgnoreFields();
super.save();
}
private void dealContentIngoreFields() {
private void dealContentIgnoreFields() {
CmsPageWidget pageWidgetEntity = this.getPageWidgetEntity();
List<RowData> rows = JacksonUtils.fromList(pageWidgetEntity.getContent(), RowData.class);
if (Objects.nonNull(rows)) {

View File

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

View File

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

View File

@ -32,7 +32,6 @@ import com.chestnut.contentcore.service.ICatalogService;
import com.chestnut.contentcore.service.IPublishPipeService;
import com.chestnut.contentcore.service.IPublishService;
import com.chestnut.contentcore.service.ISiteService;
import com.chestnut.contentcore.util.CmsPrivUtils;
import com.chestnut.contentcore.util.ConfigPropertyUtils;
import com.chestnut.contentcore.util.InternalUrlUtils;
import com.chestnut.contentcore.util.SiteUtils;
@ -46,6 +45,7 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
@ -77,8 +77,6 @@ public class CatalogController extends BaseRestController {
/**
* 查询栏目数据列表
*
* @return
*/
@GetMapping
public R<?> list() {
@ -90,9 +88,6 @@ public class CatalogController extends BaseRestController {
/**
* 查询栏目详情数据
*
* @param catalogId 栏目ID
* @return
*/
@Priv(type = AdminUserType.TYPE, value = "Catalog:View:${#catalogId}")
@GetMapping("/{catalogId}")
@ -114,10 +109,12 @@ public class CatalogController extends BaseRestController {
/**
* 新增栏目数据
*
* @param dto
* @return
*/
@Priv(
type = AdminUserType.TYPE,
value = { ContentCorePriv.ResourceView, "Site:AddCatalog:${#_header['CurrentSite']}"},
mode = SaMode.AND
)
@Log(title = "新增栏目", businessType = BusinessType.INSERT)
@PostMapping
public R<?> addCatalog(@RequestBody @Validated CatalogAddDTO dto) {
@ -129,10 +126,6 @@ public class CatalogController extends BaseRestController {
/**
* 修改栏目数据
*
* @param dto
* @return
* @throws IOException
*/
@Priv(type = AdminUserType.TYPE, value = "Catalog:Edit:${#dto.catalogId}")
@Log(title = "编辑栏目", businessType = BusinessType.UPDATE)
@ -145,9 +138,6 @@ public class CatalogController extends BaseRestController {
/**
* 删除栏目数据
*
* @param catalogId 栏目ID
* @return
*/
@Priv(type = AdminUserType.TYPE, value = "Catalog:Delete:${#catalogId}")
@Log(title = "删除", businessType = BusinessType.DELETE)
@ -168,9 +158,6 @@ public class CatalogController extends BaseRestController {
/**
* 显示/隐藏栏目
*
* @param dto
* @return
*/
@Priv(type = AdminUserType.TYPE, value = "Catalog:ShowHide:${#dto.catalogId}")
@Log(title = "显隐栏目", businessType = BusinessType.UPDATE)
@ -182,8 +169,6 @@ public class CatalogController extends BaseRestController {
/**
* 栏目树结构数据
*
* @return
*/
@GetMapping("/treeData")
public R<?> treeData() {
@ -199,20 +184,16 @@ public class CatalogController extends BaseRestController {
/**
* 内容类型数据
*
* @return
*/
@GetMapping("/getContentTypes")
public R<?> getContentTypes() {
List<Map<String, String>> list = this.contentTypes.stream().sorted((c1, c2) -> c1.getOrder() - c2.getOrder())
List<Map<String, String>> list = this.contentTypes.stream().sorted(Comparator.comparingInt(IContentType::getOrder))
.map(ct -> Map.of("id", ct.getId(), "name", I18nUtils.get(ct.getName()))).toList();
return R.ok(list);
}
/**
* 栏目类型数据
*
* @return
*/
@GetMapping("/getCatalogTypes")
public R<?> getCatalogTypes() {
@ -223,9 +204,6 @@ public class CatalogController extends BaseRestController {
/**
* 发布栏目
*
* @param dto
* @return
*/
@Priv(type = AdminUserType.TYPE, value = "Catalog:Publish:${#dto.catalogId}")
@Log(title = "发布栏目", businessType = BusinessType.OTHER)
@ -245,9 +223,6 @@ public class CatalogController extends BaseRestController {
/**
* 获取栏目扩展配置
*
* @param catalogId 栏目ID
* @return
*/
@Priv(type = AdminUserType.TYPE, value = "Catalog:View:${#catalogId}")
@GetMapping("/extends")
@ -264,10 +239,6 @@ public class CatalogController extends BaseRestController {
/**
* 保存栏目扩展配置
*
* @param catalogId 栏目ID
* @param configs 扩展配置数据
* @return
*/
@Priv(type = AdminUserType.TYPE, value = "Catalog:Edit:${#catalogId}")
@Log(title = "栏目扩展", businessType = BusinessType.UPDATE, isSaveRequestData = false)
@ -283,9 +254,6 @@ public class CatalogController extends BaseRestController {
/**
* 扩展配置应用到子栏目
*
* @param dto
* @return
*/
@Priv(type = AdminUserType.TYPE, value = "Catalog:Edit:${#dto.catalogId}")
@Log(title = "扩展配置2子栏目", businessType = BusinessType.UPDATE)
@ -301,9 +269,6 @@ public class CatalogController extends BaseRestController {
/**
* 发布通道配置应用到子栏目
*
* @param dto
* @return
*/
@Priv(type = AdminUserType.TYPE, value = "Catalog:Edit:${#dto.catalogId}")
@Log(title = "发布通道配置2子栏目", businessType = BusinessType.UPDATE)
@ -319,10 +284,6 @@ public class CatalogController extends BaseRestController {
/**
* 移动栏目
*
* @param fromCatalogId 移动的栏目ID
* @param toCatalogId 目标栏目ID
* @return
*/
@Priv(type = AdminUserType.TYPE, value = { "Catalog:Move:${#fromCatalogId}", "Catalog:Move:${#toCatalogId}" }, mode = SaMode.AND)
@Log(title = "移动栏目", businessType = BusinessType.UPDATE)

View File

@ -8,8 +8,8 @@ import com.chestnut.common.security.web.BaseRestController;
import com.chestnut.common.utils.IdUtils;
import com.chestnut.contentcore.domain.CmsContent;
import com.chestnut.contentcore.domain.CmsContentRela;
import com.chestnut.contentcore.mapper.CmsContentRelaMapper;
import com.chestnut.contentcore.service.IContentRelaService;
import com.chestnut.contentcore.service.IContentService;
import com.chestnut.system.security.AdminUserType;
import com.chestnut.system.security.StpAdminUtil;
import jakarta.validation.constraints.NotEmpty;
@ -33,19 +33,27 @@ public class ContentRelaController extends BaseRestController {
private final IContentRelaService contentRelaService;
private final CmsContentRelaMapper contentRelaMapper;
private final IContentService contentService;
@GetMapping
public R<?> getRelaContents(@RequestParam Long contentId, @RequestParam(required = false) String title) {
PageRequest pr = this.getPageRequest();
Page<CmsContent> page = contentRelaMapper.selectRelaContents(new Page<>(pr.getPageNumber(), pr.getPageSize(), true),
contentId, title);
return this.bindDataTable(page);
Page<CmsContentRela> pageResult = contentRelaService.page(
new Page<>(pr.getPageNumber(), pr.getPageSize(), true),
new LambdaQueryWrapper<CmsContentRela>().eq(CmsContentRela::getContentId, contentId)
);
if (pageResult.getRecords().size() > 0) {
List<Long> contentIds = pageResult.getRecords().stream().map(CmsContentRela::getRelaContentId).toList();
List<CmsContent> contents = this.contentService.lambdaQuery().in(CmsContent::getContentId, contentIds).list();
return this.bindDataTable(contents, pageResult.getTotal());
} else {
return this.bindDataTable(pageResult);
}
}
@PostMapping
public R<?> addRelaContents(@RequestParam Long contentId, @RequestBody List<Long> relaContentIds) {
List<Long> contentIds = contentRelaMapper.selectList(new LambdaQueryWrapper<CmsContentRela>()
List<Long> contentIds = contentRelaService.list(new LambdaQueryWrapper<CmsContentRela>()
.eq(CmsContentRela::getContentId, contentId))
.stream().map(CmsContentRela::getRelaContentId).toList();
String operator = StpAdminUtil.getLoginUser().getUsername();

View File

@ -1,21 +1,20 @@
package com.chestnut.contentcore.controller;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chestnut.common.async.AsyncTask;
import com.chestnut.common.async.AsyncTaskManager;
import com.chestnut.common.domain.R;
import com.chestnut.common.exception.CommonErrorCode;
import com.chestnut.common.i18n.I18nUtils;
import com.chestnut.common.log.annotation.Log;
import com.chestnut.common.log.enums.BusinessType;
import com.chestnut.common.security.anno.Priv;
import com.chestnut.common.security.domain.LoginUser;
import com.chestnut.common.security.web.BaseRestController;
import com.chestnut.common.utils.*;
import com.chestnut.common.utils.Assert;
import com.chestnut.common.utils.IdUtils;
import com.chestnut.common.utils.ServletUtils;
import com.chestnut.common.utils.StringUtils;
import com.chestnut.common.utils.file.FileExUtils;
import com.chestnut.contentcore.ContentCoreConsts;
import com.chestnut.contentcore.core.IProperty.UseType;
@ -33,7 +32,6 @@ import com.chestnut.contentcore.service.impl.SiteThemeService;
import com.chestnut.contentcore.util.ConfigPropertyUtils;
import com.chestnut.contentcore.util.InternalUrlUtils;
import com.chestnut.contentcore.util.SiteUtils;
import com.chestnut.system.domain.SysConfig;
import com.chestnut.system.security.AdminUserType;
import com.chestnut.system.security.StpAdminUtil;
import com.chestnut.system.validator.LongId;
@ -42,7 +40,6 @@ import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
@ -370,19 +367,8 @@ public class SiteController extends BaseRestController {
public R<?> exportSiteTheme(@Validated @RequestBody SiteExportDTO dto) {
CmsSite site = this.siteService.getSite(dto.getSiteId());
Assert.notNull(site, () -> CommonErrorCode.DATA_NOT_FOUND_BY_ID.exception("siteId", dto.getSiteId()));
// TODO
List<String> directories = new ArrayList<>();
// directories.add(site.getPath() + "/resources/");
List<CmsPublishPipe> publishPipes = this.publishPipeService.getAllPublishPipes(site.getSiteId());
publishPipes.forEach(pp -> {
String path = SiteUtils.getSitePublishPipePath(site.getPath(), pp.getCode());
directories.add(path + "assets/");
directories.add(path + "fonts/");
directories.add(path + "css/");
directories.add(path + "js/");
directories.add(path + ContentCoreConsts.TemplateDirectory);
});
AsyncTask asyncTask = this.siteExportService.exportSiteTheme(site, directories);
AsyncTask asyncTask = this.siteExportService.exportSiteTheme(site);
return R.ok(asyncTask.getTaskId());
}
@ -390,14 +376,15 @@ public class SiteController extends BaseRestController {
@PostMapping("/theme_download")
public void export(@RequestParam Long siteId, HttpServletResponse response) throws IOException {
CmsSite site = this.siteService.getSite(siteId);
File file = new File(SiteUtils.getSiteResourceRoot(site) + SiteThemeService.ThemeFileName);
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=" + SiteThemeService.ThemeFileName);
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];

View File

@ -0,0 +1,16 @@
package com.chestnut.contentcore.core;
import com.chestnut.contentcore.domain.CmsSite;
/**
* 内容核心数据引用处理器
*
* @author 兮玥
* @email 190785909@qq.com
*/
public interface ICoreDataHandler {
default void onSiteExport(SiteExportContext context) {}
default void onSiteImport(SiteImportContext context) {}
}

View File

@ -1,19 +1,14 @@
package com.chestnut.contentcore.core;
import com.chestnut.common.utils.StringUtils;
import com.chestnut.contentcore.util.TemplateUtils;
import freemarker.template.TemplateException;
import lombok.Getter;
import lombok.Setter;
import java.io.IOException;
import java.util.Map;
import cn.dev33.satoken.config.SaTokenConfig;
import com.chestnut.common.utils.ServletUtils;
import com.chestnut.common.utils.StringUtils;
import com.chestnut.system.security.StpAdminUtil;
import freemarker.template.TemplateException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
/**
* 内部数据类型
*
@ -42,13 +37,7 @@ public interface IInternalDataType {
if (pageIndex > 1) {
path += "&pi=" + pageIndex;
}
if (StpAdminUtil.isLogin()) {
SaTokenConfig config = StpAdminUtil.getStpLogic().getConfigOrGlobal();
path = path + "&" + config.getTokenName() + "="
+ (StringUtils.isNotEmpty(config.getTokenPrefix()) ? config.getTokenPrefix() + " " : "")
+ StpAdminUtil.getTokenValue();
}
return path;
return TemplateUtils.appendTokenParameter(path);
}
static String getPreviewPath(String type, Long id, String publishPipeCode) {

View File

@ -0,0 +1,27 @@
package com.chestnut.contentcore.core;
/**
* <TODO description class purpose>
*
* @author 兮玥
* @email 190785909@qq.com
*/
public interface ISiteThemeContext {
/**
* DB目录
*/
String DataDirPath = "db/";
/**
* DB文件表名序号分隔符
*/
String SPLITER = "__";
/**
* 站点资源文件目录
*/
String SiteDirPath = "wwwroot/";
}

View File

@ -0,0 +1,112 @@
package com.chestnut.contentcore.core;
import com.chestnut.contentcore.domain.CmsSite;
import com.chestnut.contentcore.util.SiteUtils;
import jodd.io.ZipBuilder;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
/**
* 站点主题导出上下文
*
* 保存导出过程中的临时数据供各模块使用
* 导出文件可保存至siteResourceRoot/_export/theme/目录下数据库数据默认位于db目录下
* theme目录下的导出临时文件会在导出逻辑最后按目录名打包
* 目前已知目录
* 站点文件wwwroot/
* 数据库文件db/
*/
@Getter
@Setter
public class SiteExportContext implements ISiteThemeContext {
/**
* 导出临时目录
*/
static final String ExportDir = "_export/theme/";
/**
* 引用资源IDS
*/
private Set<Long> resourceIds = new HashSet<>();
/**
* 导出内容ID列表
*/
private Set<Long> contentIds = new HashSet<>();
private final CmsSite site;
/**
* 是否在打包后清理临时目录
*/
private boolean clearTempFile = true;
public SiteExportContext(CmsSite site) {
this.site = site;
}
public void createZipFile(String zipPath) throws IOException {
String siteResourceRoot = SiteUtils.getSiteResourceRoot(site);
String zipFile = siteResourceRoot + zipPath;
ZipBuilder zipBuilder = ZipBuilder.createZipFile(new File(zipFile));
File exportDir = new File(siteResourceRoot + ExportDir);
File[] files = exportDir.listFiles();
if (Objects.nonNull(files)) {
for (File f : files) {
zipBuilder.add(f).path(f.getName()).recursive().save();
}
}
zipBuilder.toZipFile();
if (clearTempFile) {
this.clearTempFiles();
}
}
/**
* 保存文件到${SiteDirPath}目录
*
* @param source 源文件
* @param dest 目标路径项目资源根目录resourceRoot
*/
public void saveFile(File source, String dest) {
try {
dest = ExportDir + SiteDirPath + dest;
File destFile = new File(SiteUtils.getSiteResourceRoot(site) + dest);
if (source.isDirectory()) {
FileUtils.copyDirectory(source, destFile);
} else {
FileUtils.copyFile(source, destFile);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void saveData(String tableName, String jsonData) {
saveData(tableName, jsonData, 1);
}
public void saveData(String tableName, String jsonData, int index) {
try {
String path = ExportDir + DataDirPath + tableName + SPLITER + index + ".json";
File f = new File(SiteUtils.getSiteResourceRoot(site) + path);
FileUtils.writeStringToFile(f, jsonData, StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
}
public void clearTempFiles() throws IOException {
String siteResourceRoot = SiteUtils.getSiteResourceRoot(site);
FileUtils.deleteDirectory(new File(siteResourceRoot + ExportDir));
}
}

View File

@ -0,0 +1,117 @@
package com.chestnut.contentcore.core;
import com.chestnut.common.utils.IdUtils;
import com.chestnut.contentcore.config.CMSConfig;
import com.chestnut.contentcore.core.impl.InternalDataType_Catalog;
import com.chestnut.contentcore.core.impl.InternalDataType_Content;
import com.chestnut.contentcore.domain.CmsSite;
import com.chestnut.contentcore.util.InternalUrlUtils;
import com.chestnut.contentcore.util.SiteUtils;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
/**
* 站点主题导入上下文
*/
@Getter
@Setter
public class SiteImportContext implements ISiteThemeContext {
/**
* 导入临时目录
*/
public static final String ImportDir = "_import/theme/";
/**
* 栏目ID映射<原栏目ID导入后的栏目ID>
*/
private Map<Long, Long> catalogIdMap = new HashMap<>();
/**
* 内容ID映射<原内容ID导入后的内容ID>
*/
private Map<Long, Long> contentIdMap = new HashMap<>();
/**
* 资源ID映射<原资源ID导入后的资源ID>
*/
private Map<Long, Long> resourceIdMap = new HashMap<>();
/**
* 页面部件ID映射<原页面部件ID导入后的页面部件ID>
*/
private Map<Long, Long> pageWidgetIdMap = new HashMap<>();
private final CmsSite site;
private CmsSite sourceSite;
private String operator;
/**
* 是否在导入后清理临时目录
*/
private boolean clearTempFile = true;
public SiteImportContext(CmsSite site) {
this.site = site;
}
public List<File> readDataFiles(String tableName) {
String siteResourceRoot = SiteUtils.getSiteResourceRoot(site);
File dbDir = new File(siteResourceRoot + ImportDir + DataDirPath);
File[] files = dbDir.listFiles(f -> f.getName().startsWith(tableName + SPLITER));
if (Objects.isNull(files)) {
return List.of();
}
return Stream.of(files).toList();
}
public String dealInternalUrl(String iurl) {
InternalURL internalURL = InternalUrlUtils.parseInternalUrl(iurl);
if (Objects.nonNull(internalURL)) {
Long id = switch (internalURL.getType()) {
case InternalDataType_Catalog.ID -> catalogIdMap.get(internalURL.getId());
case InternalDataType_Content.ID -> contentIdMap.get(internalURL.getId());
default -> null;
};
if (IdUtils.validate(id)) {
internalURL.setId(id);
return internalURL.toIUrl();
}
}
return iurl;
}
public void copySiteFiles() throws IOException {
String siteResourceRoot = SiteUtils.getSiteResourceRoot(site);
// 复制资源目录文件
File source = new File(siteResourceRoot + ImportDir + SiteDirPath + sourceSite.getPath());
FileUtils.copyDirectory(source, new File(siteResourceRoot));
// 复制发布通道目录文件
File[] files = new File(siteResourceRoot + ImportDir + SiteDirPath).listFiles(f -> {
return f.getName().startsWith(sourceSite.getPath() + "_");
});
if (Objects.nonNull(files)) {
for (File file : files) {
String pp = StringUtils.substringAfterLast(file.getName(), "_");
File dest = new File(CMSConfig.getResourceRoot() + SiteUtils.getSitePublishPipePath(site.getPath(), pp));
FileUtils.copyDirectory(file, dest);
}
}
}
public void clearTempFiles() throws IOException {
String siteResourceRoot = SiteUtils.getSiteResourceRoot(site);
FileUtils.deleteDirectory(new File(siteResourceRoot + ImportDir));
}
}

View File

@ -115,6 +115,11 @@ public class ContentDTO {
*/
private String redirectUrl;
/**
* 指定标识
*/
private Long topFlag;
/**
* 内容属性值数组
*/

View File

@ -32,6 +32,16 @@ public class ListContentVO {
*/
private String title;
/*
* 短标题
*/
private String shortTitle;
/*
* 副标题
*/
private String subTitle;
/*
* 标题样式
*/

View File

@ -1,5 +1,13 @@
package com.chestnut.contentcore.fixed.dict;
import com.chestnut.common.utils.NumberUtils;
import com.chestnut.common.utils.SpringUtils;
import com.chestnut.system.domain.SysDictData;
import com.chestnut.system.fixed.FixedDictType;
import com.chestnut.system.service.ISysDictTypeService;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -7,15 +15,6 @@ import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.stereotype.Component;
import com.chestnut.common.utils.NumberUtils;
import com.chestnut.common.utils.SpringUtils;
import com.chestnut.system.domain.SysDictData;
import com.chestnut.system.fixed.FixedDictType;
import com.chestnut.system.service.ISysDictTypeService;
/**
* 启用/禁用
*/
@ -118,7 +117,7 @@ public class ContentAttribute extends FixedDictType {
}
Map<Integer, String> map = dictTypeService.selectDictDatasByType(TYPE).stream()
.collect(Collectors.toMap(d -> Integer.parseInt(d.getRemark()), SysDictData::getDictValue));
ArrayList<Integer> binaryList = NumberUtils.getBinaryList(attributes.intValue());
ArrayList<Integer> binaryList = NumberUtils.getBinaryList(attributes);
return binaryList.stream().map(map::get).toArray(String[]::new);
}
@ -135,7 +134,7 @@ public class ContentAttribute extends FixedDictType {
}
public static boolean hasAttribute(int attributes, String attrStr) {
ArrayList<Integer> binaryList = NumberUtils.getBinaryList(attributes);
return binaryList.contains(bit(attrStr));
int bit = bit(attrStr);
return (attributes & bit) == bit;
}
}

View File

@ -1,23 +1,9 @@
package com.chestnut.contentcore.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chestnut.contentcore.domain.CmsContent;
import com.chestnut.contentcore.domain.CmsContentRela;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface CmsContentRelaMapper extends BaseMapper<CmsContentRela> {
@Select("""
<script>
SELECT c.* FROM cms_content_rela a right join cms_content c on a.rela_content_id = c.content_id
WHERE a.content_id = #{contentId}
<if test=' title != null and title != "" '> AND c.title like concat('%', #{title}, '%') </if>
ORDER BY a.create_time DESC
</script>
""")
Page<CmsContent> selectRelaContents(IPage<CmsContent> page, @Param("contentId") Long contentId, @Param("title") String title);
}

View File

@ -114,14 +114,16 @@ public class SitePermissionType implements IPermissionType {
Publish(3, "发布"),
AddPageWidget(4, "新增页面部件");
AddPageWidget(4, "新增页面部件"),
AddCatalog(5, "新增栏目");
/**
* 权限项在bitset中的位置序号从0开始不可随意变更变更后会导致原权限信息错误
*/
private int bitIndex;
private final int bitIndex;
private String label;
private final String label;
SitePrivItem(int bitIndex, String label) {
this.bitIndex = bitIndex;

View File

@ -25,7 +25,6 @@ import com.chestnut.contentcore.service.ISiteService;
import com.chestnut.contentcore.util.CmsPrivUtils;
import com.chestnut.contentcore.util.ConfigPropertyUtils;
import com.chestnut.contentcore.util.SiteUtils;
import com.chestnut.system.domain.SysPermission;
import com.chestnut.system.enums.PermissionOwnerType;
import com.chestnut.system.security.StpAdminUtil;
import com.chestnut.system.service.ISysPermissionService;
@ -37,7 +36,6 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -72,17 +70,15 @@ public class SiteServiceImpl extends ServiceImpl<CmsSiteMapper, CmsSite> impleme
@Override
public CmsSite getCurrentSite(HttpServletRequest request) {
CmsSite site = null;
String siteId = ServletUtils.getHeader(request, ContentCoreConsts.Header_CurrentSite);
if (NumberUtils.isDigits(siteId)) {
try {
site = this.getSite(Long.valueOf(siteId));
} catch (Exception e) {
}
}
LoginUser loginUser = StpAdminUtil.getLoginUser();
CmsSite site = null;
Long siteId = ConvertUtils.toLong(ServletUtils.getHeader(request, ContentCoreConsts.Header_CurrentSite));
if (IdUtils.validate(siteId)
&& loginUser.hasPermission(SitePrivItem.View.getPermissionKey(siteId))) {
site = this.getSite(siteId);
}
// 无当前站点或当前站点无权限则取数据库查找一条有权限的站点数据作为当前站点
if (Objects.isNull(site) || !loginUser.hasPermission(SitePrivItem.View.getPermissionKey(site.getSiteId()))) {
if (Objects.isNull(site)) {
Optional<CmsSite> opt = this.lambdaQuery().list().stream().filter(s ->
loginUser.hasPermission(SitePrivItem.View.getPermissionKey(s.getSiteId()))).findFirst();
if (opt.isPresent()) {
@ -151,7 +147,7 @@ public class SiteServiceImpl extends ServiceImpl<CmsSiteMapper, CmsSite> impleme
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteSite(Long siteId) throws IOException {
public void deleteSite(Long siteId) {
CmsSite site = this.getById(siteId);
Assert.notNull(site, () -> CommonErrorCode.DATA_NOT_FOUND_BY_ID.exception("siteId", siteId));

View File

@ -1,49 +1,33 @@
package com.chestnut.contentcore.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chestnut.common.async.AsyncTask;
import com.chestnut.common.async.AsyncTaskManager;
import com.chestnut.common.security.domain.LoginUser;
import com.chestnut.common.utils.IdUtils;
import com.chestnut.common.utils.JacksonUtils;
import com.chestnut.common.utils.StringUtils;
import com.chestnut.contentcore.config.CMSConfig;
import com.chestnut.contentcore.core.IPageWidget;
import com.chestnut.contentcore.core.IPageWidgetType;
import com.chestnut.contentcore.core.*;
import com.chestnut.contentcore.core.impl.CatalogType_Link;
import com.chestnut.contentcore.core.impl.InternalDataType_Site;
import com.chestnut.contentcore.domain.CmsCatalog;
import com.chestnut.contentcore.domain.CmsPageWidget;
import com.chestnut.contentcore.domain.CmsPublishPipe;
import com.chestnut.contentcore.domain.CmsSite;
import com.chestnut.contentcore.domain.dto.CatalogAddDTO;
import com.chestnut.contentcore.exception.ContentCoreErrorCode;
import com.chestnut.contentcore.listener.event.SiteThemeExportEvent;
import com.chestnut.contentcore.listener.event.SiteThemeImportEvent;
import com.chestnut.contentcore.service.ICatalogService;
import com.chestnut.contentcore.service.IPageWidgetService;
import com.chestnut.contentcore.service.IPublishPipeService;
import com.chestnut.contentcore.util.ContentCoreUtils;
import com.chestnut.contentcore.domain.*;
import com.chestnut.contentcore.service.*;
import com.chestnut.contentcore.util.CatalogUtils;
import com.chestnut.contentcore.util.InternalUrlUtils;
import com.chestnut.contentcore.util.SiteUtils;
import jodd.io.ZipBuilder;
import jodd.io.ZipUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.checkerframework.checker.units.qual.C;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 站点导入导出
@ -54,114 +38,235 @@ import java.util.Map;
@Slf4j
@Service
@RequiredArgsConstructor
public class SiteThemeService implements ApplicationContextAware {
private ApplicationContext applicationContext;
public class SiteThemeService {
private final AsyncTaskManager asyncTaskManager;
private final IPublishPipeService publishPipeService;
private final ISiteService siteService;
private final ISitePropertyService sitePropertyService;
private final ICatalogService catalogService;
private final IPageWidgetService pageWidgetService;
private final IResourceService resourceService;
private final IContentService contentService;
private final List<ICoreDataHandler> contentCoreHandlers;
public AsyncTask importSiteTheme(CmsSite site, final File zipFile, LoginUser operator) throws IOException {
// TODO 校验数据必须无栏目内容页面部件等数据的站点才能导入
AsyncTask asyncTask = new AsyncTask() {
@Override
public void run0() throws Exception {
SiteImportContext context = new SiteImportContext(site);
context.setOperator(operator.getUsername());
// 解压导入zip包
AsyncTaskManager.setTaskProgressInfo(10, "正在解压");
String destDir = SiteUtils.getSiteResourceRoot(site) + "siteTheme/";
String destDir = SiteUtils.getSiteResourceRoot(site) + SiteImportContext.ImportDir;
ZipUtil.unzip(zipFile, new File(destDir));
try {
// 校验可能冲突的数据
Map<Long, CmsCatalog> catalogIdMap = new HashMap<>(); // <原ID, 新ID>
// 导入数据
AsyncTaskManager.setTaskProgressInfo(20, "正在导入栏目数据");
File file = new File(destDir + "db/" + CmsCatalog.TABLE_NAME + ".json");
if (file.exists()) {
List<File> files = context.readDataFiles(CmsSite.TABLE_NAME);
CmsSite sourceSite = JacksonUtils.from(files.get(0), CmsSite.class);
context.setSourceSite(sourceSite);
// 导入站点扩展属性
AsyncTaskManager.setTaskProgressInfo(5, "正在导入站点扩展属性数据");
files = context.readDataFiles(CmsSiteProperty.TABLE_NAME);
files.forEach(file -> {
List<CmsSiteProperty> list = JacksonUtils.fromList(file, CmsSiteProperty.class);
list.forEach(data -> {
Long propertyId = data.getPropertyId();
try {
data.setPropertyId(IdUtils.getSnowflakeId());
data.setSiteId(site.getSiteId());
data.createBy(context.getOperator());
sitePropertyService.save(data);
} catch (Exception e) {
this.addErrorMessage("导入站点扩展属性数据失败:" + propertyId);
e.printStackTrace();
}
});
});
// 导入素材数据
AsyncTaskManager.setTaskProgressInfo(20, "正在导入资源数据");
files = context.readDataFiles(CmsResource.TABLE_NAME);
files.forEach(file -> {
List<CmsResource> list = JacksonUtils.fromList(file, CmsResource.class);
list.forEach(data -> {
Long oldResource = data.getResourceId();
try {
data.setResourceId(IdUtils.getSnowflakeId());
data.setSiteId(site.getSiteId());
data.createBy(context.getOperator());
resourceService.save(data);
context.getResourceIdMap().put(oldResource, data.getResourceId());
} catch (Exception e) {
this.addErrorMessage("导入素材资源数据失败:" + oldResource);
e.printStackTrace();
}
});
});
// 处理站点数据
site.setConfigProps(sourceSite.getConfigProps());
site.setPublishPipeProps(sourceSite.getPublishPipeProps());
site.setResourceUrl(sourceSite.getResourceUrl());
site.setDescription(sourceSite.getDescription());
site.setSeoTitle(sourceSite.getSeoTitle());
site.setSeoKeywords(sourceSite.getSeoKeywords());
site.setSeoDescription(sourceSite.getSeoDescription());
site.setLogo(context.dealInternalUrl(sourceSite.getLogo()));
siteService.updateById(site);
siteService.clearCache(site.getSiteId());
// 导入栏目数据
List<CmsCatalog> linkCatalogs = new ArrayList<>();
files = context.readDataFiles(CmsCatalog.TABLE_NAME);
files.forEach(file -> {
List<CmsCatalog> list = JacksonUtils.fromList(file, CmsCatalog.class);
// 必须保证顺序
list.sort(Comparator.comparing(CmsCatalog::getAncestors));
list.forEach(data -> {
try {
CatalogAddDTO dto = new CatalogAddDTO();
dto.setSiteId(site.getSiteId());
dto.setName(data.getName());
dto.setPath(data.getPath());
dto.setAlias(data.getAlias());
dto.setCatalogType(data.getCatalogType());
if (data.getParentId() > 0) {
dto.setParentId(catalogIdMap.get(data.getParentId()).getCatalogId());
Long sourceCatalogId = data.getCatalogId();
data.setCatalogId(IdUtils.getSnowflakeId());
data.setSiteId(site.getSiteId());
data.setAncestors(data.getCatalogId().toString());
if (IdUtils.validate(data.getParentId())) {
Long pCatalogId = context.getCatalogIdMap().get(data.getParentId());
CmsCatalog parent = catalogService.getCatalog(pCatalogId);
if (Objects.nonNull(parent)) {
data.setParentId(parent.getCatalogId());
data.setAncestors(CatalogUtils.getCatalogAncestors(parent, data.getCatalogId()));
} else {
data.setParentId(0L);
}
}
dto.setOperator(operator);
CmsCatalog catalog = catalogService.addCatalog(dto);
catalog.setConfigProps(data.getConfigProps());
catalog.setPublishPipeProps(data.getPublishPipeProps());
catalog.setDescription(data.getDescription());
catalog.setSeoTitle(data.getSeoTitle());
catalog.setSeoKeywords(data.getSeoKeywords());
catalog.setSeoDescription(data.getSeoDescription());
if (CatalogType_Link.ID.equals(catalog.getCatalogType())) {
// 链接栏目统一设置成站点链接
catalog.setRedirectUrl(InternalUrlUtils.getInternalUrl(InternalDataType_Site.ID, site.getSiteId()));
data.createBy(context.getOperator());
// 处理logo
InternalURL internalURL = InternalUrlUtils.parseInternalUrl(data.getLogo());
if (Objects.nonNull(internalURL)) {
Long resourceId = context.getResourceIdMap().get(internalURL.getId());
if (IdUtils.validate(resourceId)) {
internalURL.setId(resourceId);
data.setLogo(internalURL.toIUrl());
} else {
data.setLogo(StringUtils.EMPTY);
}
}
catalogService.save(data);
context.getCatalogIdMap().put(sourceCatalogId, data.getCatalogId());
if (CatalogType_Link.ID.equals(data.getCatalogType())) {
linkCatalogs.add(data);
}
catalogService.updateById(catalog);
catalogIdMap.put(data.getCatalogId(), catalog);
} catch (Exception e) {
this.addErrorMessage("栏目添加失败:" + data.getName() + " > " + e.getMessage());
this.addErrorMessage("导入栏目数据失败:" + data.getName());
e.printStackTrace();
}
});
}
});
AsyncTaskManager.setTaskProgressInfo(30, "正在导入发布通道数据");
file = new File(destDir + "db/" + CmsPublishPipe.TABLE_NAME + ".json");
if (file.exists()) {
files = context.readDataFiles(CmsPublishPipe.TABLE_NAME);
files.forEach(file -> {
List<CmsPublishPipe> list = JacksonUtils.fromList(file, CmsPublishPipe.class);
for (CmsPublishPipe data : list) {
try {
data.setPublishpipeId(IdUtils.getSnowflakeId());
data.setSiteId(site.getSiteId());
data.setCreateBy(operator.getUsername());
publishPipeService.addPublishPipe(data);
} catch (Exception e) {
this.addErrorMessage("发布通道添加失败:" + data.getName() + " > " + e.getMessage());
this.addErrorMessage("导入发布通道数据失败:" + data.getName());
e.printStackTrace();
}
}
}
});
AsyncTaskManager.setTaskProgressInfo(50, "正在导入页面部件数据");
file = new File(destDir + "db/" + CmsPageWidget.TABLE_NAME + ".json");
if (file.exists()) {
files = context.readDataFiles(CmsPageWidget.TABLE_NAME);
files.forEach(file -> {
List<CmsPageWidget> list = JacksonUtils.fromList(file, CmsPageWidget.class);
list.forEach(data -> {
try {
IPageWidgetType pwt = ContentCoreUtils.getPageWidgetType(data.getType());
IPageWidget pw = pwt.newInstance();
pw.setPageWidgetEntity(data);
pw.setOperator(operator);
Long oldPageWidgetId = data.getPageWidgetId();
data.setPageWidgetId(IdUtils.getSnowflakeId());
data.setSiteId(site.getSiteId());
if (IdUtils.validate(data.getCatalogId())) {
CmsCatalog catalog = catalogIdMap.get(data.getCatalogId());
pw.getPageWidgetEntity().setCatalogId(catalog.getCatalogId());
pw.getPageWidgetEntity().setCatalogAncestors(catalog.getAncestors());
Long catalogId = context.getCatalogIdMap().get(data.getCatalogId());
CmsCatalog catalog = catalogService.getCatalog(catalogId);
if (Objects.nonNull(catalog)) {
data.setCatalogId(catalog.getCatalogId());
data.setCatalogAncestors(catalog.getAncestors());
}
}
pw.getPageWidgetEntity().setSiteId(site.getSiteId());
pw.add();
data.createBy(context.getOperator());
pageWidgetService.save(data);
context.getPageWidgetIdMap().put(oldPageWidgetId, data.getPageWidgetId());
} catch (Exception e) {
this.addErrorMessage("页面部件添加失败:" + data.getName() + " > " + e.getMessage());
this.addErrorMessage("导入页面部件数据失败:" + data.getName());
e.printStackTrace();
}
});
}
});
// 导入内容数据
List<CmsContent> linkContents = new ArrayList<>();
AsyncTaskManager.setTaskProgressInfo(50, "正在导入内容数据");
files = context.readDataFiles(CmsContent.TABLE_NAME);
files.forEach(file -> {
List<CmsContent> list = JacksonUtils.fromList(file, CmsContent.class);
list.forEach(content -> {
Long sourceContentId = content.getContentId();
try {
CmsCatalog catalog = catalogService.getCatalog(context.getCatalogIdMap().get(content.getCatalogId()));
content.setContentId(IdUtils.getSnowflakeId());
content.setSiteId(site.getSiteId());
content.setCatalogId(catalog.getCatalogId());
content.setCatalogAncestors(catalog.getAncestors());
String topCatalogIdStr = catalog.getAncestors().split(CatalogUtils.ANCESTORS_SPLITER)[0];
content.setTopCatalog(Long.valueOf(topCatalogIdStr));
content.setDeptId(0L);
content.setDeptCode(StringUtils.EMPTY);
content.createBy(operator.getUsername());
// 处理logo
InternalURL internalURL = InternalUrlUtils.parseInternalUrl(catalog.getLogo());
if (Objects.nonNull(internalURL)) {
Long resourceId = context.getResourceIdMap().get(internalURL.getId());
if (IdUtils.validate(resourceId)) {
internalURL.setId(resourceId);
catalog.setLogo(internalURL.toIUrl());
} else {
catalog.setLogo(StringUtils.EMPTY);
}
}
contentService.save(content);
context.getContentIdMap().put(sourceContentId, content.getContentId());
if (content.isLinkContent()) {
linkContents.add(content);
}
} catch (Exception e) {
this.addErrorMessage("导入内容数据失败:" + sourceContentId);
e.printStackTrace();
}
});
});
// 处理链接栏目的内部链接地址
linkCatalogs.forEach(catalog -> {
String iurl = context.dealInternalUrl(catalog.getRedirectUrl());
catalogService.lambdaUpdate().set(CmsCatalog::getRedirectUrl, iurl)
.eq(CmsCatalog::getCatalogId, catalog.getCatalogId()).update();
});
linkContents.forEach(content -> {
String iurl = context.dealInternalUrl(content.getRedirectUrl());
contentService.lambdaUpdate().set(CmsContent::getRedirectUrl, iurl)
.eq(CmsContent::getContentId, content.getContentId()).update();
});
// 其他模块数据处理
contentCoreHandlers.forEach(h -> h.onSiteImport(context));
// 复制文件
AsyncTaskManager.setTaskProgressInfo(50, "正在导入发布通道相关文件");
String sitePath = FileUtils.readFileToString(new File(destDir + "site.txt"), Charset.defaultCharset());
File wwwroot = new File(destDir + "wwwroot");
File[] wwwrootFiles = wwwroot.listFiles();
if (wwwrootFiles != null) {
for (File f : wwwrootFiles) {
String dest = CMSConfig.getResourceRoot() + f.getName().replaceFirst(sitePath, site.getPath());
FileUtils.copyDirectory(f, new File(dest));
}
}
applicationContext.publishEvent(new SiteThemeImportEvent(this, site, destDir, operator));
context.copySiteFiles();
} finally {
FileUtils.deleteDirectory(new File(destDir));
this.setProgressInfo(100, "导入完成");
@ -174,51 +279,52 @@ public class SiteThemeService implements ApplicationContextAware {
return asyncTask;
}
public static final String ThemeFileName = "SiteTheme.zip";
public static final String ThemeZipPath = "_export/SiteTheme.zip";
public AsyncTask exportSiteTheme(CmsSite site, final List<String> directories) {
public AsyncTask exportSiteTheme(CmsSite site) {
AsyncTask asyncTask = new AsyncTask() {
@Override
public void run0() throws Exception {
String siteResourceRoot = SiteUtils.getSiteResourceRoot(site);
String zipFile = siteResourceRoot + ThemeFileName;
ZipBuilder zipBuilder = ZipBuilder.createZipFile(new File(zipFile));
// 记录下原站点目录
zipBuilder.add(site.getPath()).path("site.txt").save();
// 发布通道文件
AsyncTaskManager.setTaskProgressInfo(10, "正在导出站点相关文件");
final String prefix = "wwwroot/";
for (int i = 0; i < directories.size(); i++) {
String directory = directories.get(i);
File files = new File(CMSConfig.getResourceRoot() + directory);
if (files.exists()) {
zipBuilder.add(files).path(prefix + directory).recursive().save();
SiteExportContext context = new SiteExportContext(site);
// cms_site
{
AsyncTaskManager.setTaskProgressInfo(0, "正在导出站点数据");
String json = JacksonUtils.to(site);
context.saveData(CmsSite.TABLE_NAME, json);
// 记录资源引用
InternalURL internalURL = InternalUrlUtils.parseInternalUrl(site.getLogo());
if (Objects.nonNull(internalURL)) {
context.getResourceIds().add(internalURL.getId());
}
}
// 仅导出发布通道栏目和页面部件基础数据
// cms_publishpipe
// cms_site_property
{
AsyncTaskManager.setTaskProgressInfo(20, "正在导出发布通道数据");
List<CmsPublishPipe> list = publishPipeService.lambdaQuery()
.eq(CmsPublishPipe::getSiteId, site.getSiteId())
AsyncTaskManager.setTaskProgressInfo(5, "正在导出站点扩展属性数据");
List<CmsSiteProperty> list = sitePropertyService.lambdaQuery()
.eq(CmsSiteProperty::getSiteId, site.getSiteId())
.list();
String json = JacksonUtils.to(list);
zipBuilder.add(json.getBytes(StandardCharsets.UTF_8))
.path("db/" + CmsPublishPipe.TABLE_NAME + ".json")
.save();
context.saveData(CmsSiteProperty.TABLE_NAME, json);
}
List<String> catalogPaths = new ArrayList<>();
// cms_catalog
{
AsyncTaskManager.setTaskProgressInfo(30, "正在导出栏目数据");
List<CmsCatalog> list = catalogService.lambdaQuery()
.eq(CmsCatalog::getSiteId, site.getSiteId())
.orderByAsc(CmsCatalog::getAncestors)
.orderByAsc(CmsCatalog::getAncestors) // 必须保证顺序
.list();
String json = JacksonUtils.to(list);
zipBuilder.add(json.getBytes(StandardCharsets.UTF_8))
.path("db/" + CmsCatalog.TABLE_NAME + ".json")
.save();
context.saveData(CmsCatalog.TABLE_NAME, json);
list.forEach(catalog -> {
InternalURL internalURL = InternalUrlUtils.parseInternalUrl(catalog.getLogo());
if (Objects.nonNull(internalURL)) {
context.getResourceIds().add(internalURL.getId());
}
catalogPaths.add(catalog.getPath());
});
}
// cms_page_widget
{
@ -227,12 +333,95 @@ public class SiteThemeService implements ApplicationContextAware {
.eq(CmsPageWidget::getSiteId, site.getSiteId())
.list();
String json = JacksonUtils.to(list);
zipBuilder.add(json.getBytes(StandardCharsets.UTF_8))
.path("db/" + CmsPageWidget.TABLE_NAME + ".json")
.save();
context.saveData(CmsPageWidget.TABLE_NAME, json);
}
applicationContext.publishEvent(new SiteThemeExportEvent(this, site, zipBuilder));
zipBuilder.toZipFile();
// cms_content
{
AsyncTaskManager.setTaskProgressInfo(40, "正在导出内容数据");
long offset = 0;
int pageSize = 200;
int fileIndex = 1;
while (true) {
LambdaQueryWrapper<CmsContent> q = new LambdaQueryWrapper<CmsContent>()
.eq(CmsContent::getSiteId, site.getSiteId())
.gt(CmsContent::getContentId, offset)
.orderByAsc(CmsContent::getContentId);
Page<CmsContent> page = contentService.page(new Page<>(1, pageSize, false), q);
if (page.getRecords().size() > 0) {
context.saveData(CmsContent.TABLE_NAME, JacksonUtils.to(page.getRecords()), fileIndex);
offset = page.getRecords().get(page.getRecords().size() - 1).getContentId();
fileIndex++;
page.getRecords().forEach(content -> {
InternalURL internalURL = InternalUrlUtils.parseInternalUrl(content.getLogo());
if (Objects.nonNull(internalURL)) {
context.getResourceIds().add(internalURL.getId());
}
});
if (page.getRecords().size() < pageSize) {
break;
}
} else {
break;
}
}
}
// 导出关联资源cms_resource
{
AsyncTaskManager.setTaskProgressInfo(40, "正在导出素材数据");
long offset = 0;
int pageSize = 200;
int fileIndex = 1;
while (true) {
LambdaQueryWrapper<CmsResource> q = new LambdaQueryWrapper<CmsResource>()
.eq(CmsResource::getSiteId, site.getSiteId())
.gt(CmsResource::getResourceId, offset)
.orderByAsc(CmsResource::getResourceId);
Page<CmsResource> page = resourceService.page(new Page<>(1, pageSize, false), q);
if (page.getRecords().size() > 0) {
context.saveData(CmsResource.TABLE_NAME, JacksonUtils.to(page.getRecords()), fileIndex);
offset = page.getRecords().get(page.getRecords().size() - 1).getResourceId();
fileIndex++;
if (page.getRecords().size() < pageSize) {
break;
}
} else {
break;
}
}
}
// cms_publishpipe
AsyncTaskManager.setTaskProgressInfo(20, "正在导出发布通道数据");
List<CmsPublishPipe> publishPipes = publishPipeService.lambdaQuery()
.eq(CmsPublishPipe::getSiteId, site.getSiteId())
.list();
context.saveData(CmsPublishPipe.TABLE_NAME, JacksonUtils.to(publishPipes));
// 发布通道模板资源保存到导出临时目录
final String resourceRoot = CMSConfig.getResourceRoot();
publishPipes.forEach(pp -> {
String ppPath = SiteUtils.getSitePublishPipePath(site.getPath(), pp.getCode());
File[] files = new File(resourceRoot + ppPath).listFiles((dir, name) -> {
// 过滤掉栏目目录和include目录
for (String catalogPath : catalogPaths) {
if (catalogPath.startsWith(name + "/") || "include".equals(name)) {
return false;
}
}
return true;
});
if (files != null) {
for (File file : files) {
context.saveFile(file, file.getAbsolutePath().substring(resourceRoot.length()));
}
}
});
// 素材资源目录复制到导出临时目录
context.saveFile(new File(siteResourceRoot + IResourceType.UploadResourceDirectory),
SiteUtils.getSiteResourcePath(site.getPath()) + IResourceType.UploadResourceDirectory);
// 其他模块数据处理
contentCoreHandlers.forEach(h -> h.onSiteExport(context));
// ############ 导出压缩文件 #################
context.createZipFile(ThemeZipPath);
// context.clearTempFiles();
AsyncTaskManager.setTaskProgressInfo(100, "导出成功");
}
};
@ -241,9 +430,4 @@ public class SiteThemeService implements ApplicationContextAware {
this.asyncTaskManager.execute(asyncTask);
return asyncTask;
}
@Override
public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

View File

@ -1,23 +1,27 @@
package com.chestnut.contentcore.template.func;
import com.chestnut.common.staticize.func.AbstractFunc;
import com.chestnut.common.utils.ConvertUtils;
import com.chestnut.common.utils.DateUtils;
import com.chestnut.common.utils.NumberUtils;
import com.chestnut.common.utils.StringUtils;
import freemarker.ext.beans.BeanModel;
import freemarker.template.SimpleNumber;
import freemarker.template.SimpleScalar;
import freemarker.template.TemplateModelException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import org.springframework.stereotype.Component;
import com.chestnut.common.staticize.func.AbstractFunc;
import com.chestnut.common.utils.ConvertUtils;
import com.chestnut.common.utils.DateUtils;
import com.chestnut.common.utils.StringUtils;
import freemarker.ext.beans.BeanModel;
import freemarker.template.TemplateModelException;
import lombok.RequiredArgsConstructor;
/**
* Freemarker模板自定义函数日期格式化
*/
@ -26,7 +30,7 @@ import lombok.RequiredArgsConstructor;
public class DateFormatFunction extends AbstractFunc {
static final String FUNC_NAME = "dateFormat";
private static final String DESC = "{FREEMARKER.FUNC.DESC." + FUNC_NAME + "}";
private static final SimpleDateFormat DEFAULT_SIMPLE_DATE_FORMAT = new SimpleDateFormat(
@ -64,6 +68,18 @@ public class DateFormatFunction extends AbstractFunc {
return DEFAULT_SIMPLE_DATE_FORMAT.format(d);
}
}
} else if (args[0] instanceof SimpleScalar s) {
String value = s.getAsString();
if (NumberUtils.isCreatable(value)) {
LocalDateTime dateTime = Instant.ofEpochMilli(ConvertUtils.toLong(value))
.atZone(ZoneId.systemDefault()).toLocalDateTime();
return DateTimeFormatter.ofPattern(args[1].toString()).format(dateTime);
}
} else if (args[0] instanceof SimpleNumber s) {
long value = s.getAsNumber().longValue();
LocalDateTime dateTime = Instant.ofEpochMilli(value)
.atZone(ZoneId.systemDefault()).toLocalDateTime();
return DateTimeFormatter.ofPattern(args[1].toString()).format(dateTime);
}
return args[0].toString();
}

View File

@ -1,6 +1,5 @@
package com.chestnut.contentcore.template.func;
import cn.dev33.satoken.config.SaTokenConfig;
import com.chestnut.common.staticize.FreeMarkerUtils;
import com.chestnut.common.staticize.core.TemplateContext;
import com.chestnut.common.staticize.func.AbstractFunc;
@ -8,7 +7,7 @@ import com.chestnut.common.utils.Assert;
import com.chestnut.common.utils.StringUtils;
import com.chestnut.contentcore.core.IDynamicPageType;
import com.chestnut.contentcore.exception.ContentCoreErrorCode;
import com.chestnut.system.security.StpAdminUtil;
import com.chestnut.contentcore.util.TemplateUtils;
import freemarker.core.Environment;
import freemarker.template.TemplateBooleanModel;
import freemarker.template.TemplateModelException;
@ -58,12 +57,7 @@ public class DynamicPageLinkFunction extends AbstractFunc {
if (context.isPreview()) {
path += "?preview=true&sid=" + FreeMarkerUtils.evalLongVariable(Environment.getCurrentEnvironment(), "Site.siteId")
+ "&pp=" + context.getPublishPipeCode();
if (StpAdminUtil.isLogin()) {
SaTokenConfig config = StpAdminUtil.getStpLogic().getConfigOrGlobal();
path += "&" + config.getTokenName() + "="
+ (StringUtils.isNotEmpty(config.getTokenPrefix()) ? config.getTokenPrefix() + " " : "")
+ StpAdminUtil.getTokenValue();;
}
path = TemplateUtils.appendTokenParameter(path, Environment.getCurrentEnvironment());
} else {
if (!ignoreBaseArg) {
path += "?sid=" + FreeMarkerUtils.evalLongVariable(Environment.getCurrentEnvironment(), "Site.siteId")

View File

@ -1,11 +1,17 @@
package com.chestnut.contentcore.template.tag;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chestnut.common.staticize.FreeMarkerUtils;
import com.chestnut.common.staticize.core.TemplateContext;
import com.chestnut.common.staticize.enums.TagAttrDataType;
import com.chestnut.common.staticize.tag.AbstractListTag;
import com.chestnut.common.staticize.tag.TagAttr;
import com.chestnut.contentcore.domain.CmsContent;
import com.chestnut.contentcore.domain.CmsContentRela;
import com.chestnut.contentcore.domain.dto.ContentDTO;
import com.chestnut.contentcore.mapper.CmsContentRelaMapper;
import com.chestnut.contentcore.service.IContentService;
import freemarker.core.Environment;
import freemarker.template.TemplateException;
import lombok.RequiredArgsConstructor;
@ -27,6 +33,8 @@ public class CmsContentRelaTag extends AbstractListTag {
private final CmsContentRelaMapper contentRelaMapper;
private final IContentService contentService;
@Override
public List<TagAttr> getTagAttrs() {
List<TagAttr> tagAttrs = super.getTagAttrs();
@ -41,9 +49,22 @@ public class CmsContentRelaTag extends AbstractListTag {
if (contentId <= 0) {
throw new TemplateException("内容ID错误" + contentId, env);
}
Page<CmsContent> pageResult = contentRelaMapper
.selectRelaContents(new Page<>(pageIndex, size, page), contentId, null);
return TagPageData.of(pageResult.getRecords(), pageResult.getTotal());
TemplateContext context = FreeMarkerUtils.getTemplateContext(env);
Page<CmsContentRela> pageResult = contentRelaMapper.selectPage(new Page<>(pageIndex, size, page),
new LambdaQueryWrapper<CmsContentRela>().eq(CmsContentRela::getContentId, contentId));
if (pageResult.getRecords().size() > 0) {
List<Long> contentIds = pageResult.getRecords().stream().map(CmsContentRela::getRelaContentId).toList();
List<CmsContent> contents = this.contentService.lambdaQuery().in(CmsContent::getContentId, contentIds).list();
List<ContentDTO> result = contents.stream().map(c -> {
ContentDTO dto = ContentDTO.newInstance(c);
dto.setLink(this.contentService.getContentLink(c, 1,
context.getPublishPipeCode(), context.isPreview()));
return dto;
}).toList();
return TagPageData.of(result, page ? pageResult.getTotal() : result.size());
} else {
return TagPageData.of(List.of(), 0);
}
}
@Override

View File

@ -1,5 +1,27 @@
package com.chestnut.contentcore.template.tag;
import com.chestnut.common.staticize.FreeMarkerUtils;
import com.chestnut.common.staticize.core.TemplateContext;
import com.chestnut.common.staticize.enums.TagAttrDataType;
import com.chestnut.common.staticize.tag.AbstractTag;
import com.chestnut.common.staticize.tag.TagAttr;
import com.chestnut.common.utils.Assert;
import com.chestnut.common.utils.StringUtils;
import com.chestnut.contentcore.domain.CmsSite;
import com.chestnut.contentcore.properties.EnableSSIProperty;
import com.chestnut.contentcore.service.ISiteService;
import com.chestnut.contentcore.service.ITemplateService;
import com.chestnut.contentcore.util.SiteUtils;
import com.chestnut.contentcore.util.TemplateUtils;
import freemarker.core.Environment;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
@ -10,31 +32,6 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import com.chestnut.common.utils.ServletUtils;
import com.chestnut.contentcore.properties.EnableSSIProperty;
import com.chestnut.contentcore.util.TemplateUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Component;
import com.chestnut.common.staticize.FreeMarkerUtils;
import com.chestnut.common.staticize.core.TemplateContext;
import com.chestnut.common.staticize.enums.TagAttrDataType;
import com.chestnut.common.staticize.tag.AbstractTag;
import com.chestnut.common.staticize.tag.TagAttr;
import com.chestnut.common.utils.Assert;
import com.chestnut.common.utils.StringUtils;
import com.chestnut.contentcore.domain.CmsSite;
import com.chestnut.contentcore.service.ISiteService;
import com.chestnut.contentcore.service.ITemplateService;
import com.chestnut.contentcore.util.SiteUtils;
import freemarker.core.Environment;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Component
public class CmsIncludeTag extends AbstractTag {
@ -74,7 +71,7 @@ public class CmsIncludeTag extends AbstractTag {
/**
* 使用virtual来访问动态区块内容实际访问后台/ssi/virtual接口返回html内容后包含在前端页面中
* 与ajax异步获取内容不同此方式可将动态内容与html同步返回有利于Spider获取页面更新 包含模板中可用${Request.xxx}获取参数值
*
*
* <@cms_include virtual="footer.template.html?t=123&c=ddd"></@cms_include>
*/
public static final String SSI_INCLUDE_VIRTUAL_TAG = "<!--#include virtual=\"{0}\" -->\n";
@ -87,7 +84,7 @@ public class CmsIncludeTag extends AbstractTag {
public List<TagAttr> getTagAttrs() {
List<TagAttr> tagAttrs = new ArrayList<>();
tagAttrs.add(new TagAttr(TagAttr_FILE, true, TagAttrDataType.STRING, "引用模板文件路径相对模板目录template/"));
tagAttrs.add(new TagAttr(TagAttr_SSI, false, TagAttrDataType.BOOLEAN, "是否启用SSI", "true"));
tagAttrs.add(new TagAttr(TagAttr_SSI, false, TagAttrDataType.BOOLEAN, "是否启用SSI"));
tagAttrs.add(new TagAttr(TagAttr_VIRTUAL, false, TagAttrDataType.BOOLEAN, "是否启用virtual此模式下区块无法继承当前页面上限文变量需要通过参数传入需要的变量", "false"));
tagAttrs.add(new TagAttr(TagAttr_CACHE, false, TagAttrDataType.BOOLEAN, "是否启用缓存", "true"));

View File

@ -74,7 +74,7 @@ public class CmsPageWidgetTag extends AbstractTag {
public List<TagAttr> getTagAttrs() {
List<TagAttr> tagAttrs = new ArrayList<>();
tagAttrs.add(new TagAttr(TagAttr_Code, true, TagAttrDataType.STRING, "页面部件编码"));
tagAttrs.add(new TagAttr(TagAttr_SSI, false, TagAttrDataType.BOOLEAN, "是否启用SSI", "true"));
tagAttrs.add(new TagAttr(TagAttr_SSI, false, TagAttrDataType.BOOLEAN, "是否启用SSI"));
return tagAttrs;
}

View File

@ -13,9 +13,9 @@ public class SiteUtils {
/**
* 获取站点指定发布通道根目录路径静态化文件目录
*
* @param site
* @param publishPipeCode
* @return
* @param site 站点
* @param publishPipeCode 发布通道编码
* @return 站点发布通道绝对路径
*/
public static String getSiteRoot(CmsSite site, String publishPipeCode) {
return CMSConfig.getResourceRoot() + getSitePublishPipePath(site.getPath(), publishPipeCode);
@ -23,8 +23,6 @@ public class SiteUtils {
/**
* 获取站点发布通道访问链接前缀
*
* @return
*/
public static String getPublishPipePrefix(CmsSite site, String publishPipeCode, boolean isPreview) {
if (isPreview) {
@ -37,9 +35,8 @@ public class SiteUtils {
/**
* 站点发布通道相对资源根目录路径 = 站点目录名 + "_" + 发布通道编码 + "/"
*
* @param sitePath
* @param publishPipeCode
* @return
* @param sitePath 站点目录名
* @param publishPipeCode 发布通道编码
*/
public static String getSitePublishPipePath(String sitePath, String publishPipeCode) {
return sitePath + "_" + publishPipeCode + StringUtils.SLASH;
@ -48,8 +45,7 @@ public class SiteUtils {
/**
* 获取站点资源文件根目录
*
* @param sitePath
* @return
* @param sitePath 站点目录名
*/
public static String getSiteResourceRoot(String sitePath) {
return CMSConfig.getResourceRoot() + getSiteResourcePath(sitePath);
@ -62,9 +58,8 @@ public class SiteUtils {
/**
* 获取站点资源文件访问链接前缀
*
* @param site
* @param isPreview
* @return
* @param site 站点
* @param isPreview 是否预览模式
*/
public static String getResourcePrefix(CmsSite site, boolean isPreview) {
if (isPreview || StringUtils.isEmpty(site.getResourceUrl())) {
@ -76,8 +71,7 @@ public class SiteUtils {
/**
* 获取站点资源相对资源根目录路径
*
* @param sitePath
* @return
* @param sitePath 站点目录名
*/
public static String getSiteResourcePath(String sitePath) {
return sitePath + StringUtils.SLASH;
@ -86,15 +80,14 @@ public class SiteUtils {
/**
* 获取站点访问链接
*
* @param site
* @param publishPipeCode
* @param isPreview
* @return
* @param site 站点
* @param publishPipeCode 发布通道编码
* @param isPreview 是否预览模式
*/
public static String getSiteLink(CmsSite site, String publishPipeCode, boolean isPreview) {
if (isPreview) {
String previewPath = IInternalDataType.getPreviewPath(InternalDataType_Site.ID,
site.getSiteId(), publishPipeCode, 1);
site.getSiteId(), publishPipeCode);
return BackendContext.getValue() + previewPath;
}
return site.getUrl(publishPipeCode);
@ -103,10 +96,9 @@ public class SiteUtils {
/**
* 获取模板相对项目资源根目录ResourceRoot的路径
*
* @param site
* @param site 站点
* @param publishPipeCode 发布通道编码
* @param template 模板文件路径相对于站点模板目录路径
* @return
*/
public static String getTemplateKey(CmsSite site, String publishPipeCode, String template) {
return site.getPath() + "_" + publishPipeCode + StringUtils.SLASH + ContentCoreConsts.TemplateDirectory + template;

View File

@ -1,5 +1,7 @@
package com.chestnut.contentcore.util;
import cn.dev33.satoken.config.SaTokenConfig;
import com.chestnut.common.staticize.FreeMarkerUtils;
import com.chestnut.common.staticize.core.TemplateContext;
import com.chestnut.common.utils.ReflectASMUtils;
import com.chestnut.common.utils.StringUtils;
@ -9,6 +11,9 @@ import com.chestnut.contentcore.domain.CmsCatalog;
import com.chestnut.contentcore.domain.CmsSite;
import com.chestnut.contentcore.fixed.config.TemplateSuffix;
import com.chestnut.contentcore.properties.SiteApiUrlProperty;
import com.chestnut.system.security.StpAdminUtil;
import freemarker.core.Environment;
import freemarker.template.TemplateModelException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@ -17,7 +22,17 @@ import java.util.Map;
@Component
@RequiredArgsConstructor
public class TemplateUtils {
/**
* 模板变量预览模式登录用户token键名
*/
public final static String TemplateVariable_TokenName = "TokenName";
/**
* 模板变量预览模式登录用户token
*/
public final static String TemplateVariable_Token = "Token";
/**
* 模板变量是否预览模式
*/
@ -93,7 +108,7 @@ public class TemplateUtils {
/**
* 添加栏目数据到模板上下文变量中
*
*
* @param site 站点
* @param catalog 栏目
* @param context 模板上下文
@ -133,8 +148,45 @@ public class TemplateUtils {
context.getVariables().put(TemplateVariable_ApiPrefix, SiteApiUrlProperty.getValue(site, context.getPublishPipeCode()));
// 添加站点数据
addSiteVariables(site, context);
if (context.isPreview()) {
SaTokenConfig config = StpAdminUtil.getStpLogic().getConfigOrGlobal();
context.getVariables().put(TemplateVariable_TokenName, config.getTokenName());
String token = StpAdminUtil.getTokenValue();
if (StringUtils.isNotEmpty(config.getTokenPrefix())) {
token = config.getTokenPrefix() + " " + token;
}
context.getVariables().put(TemplateVariable_Token, token);
}
}
public static String appendTokenParameter(String url, Environment env) throws TemplateModelException {
if (StringUtils.isEmpty(url)) {
return url;
}
String tokenName = FreeMarkerUtils.getStringVariable(Environment.getCurrentEnvironment(), TemplateUtils.TemplateVariable_TokenName);
String token = FreeMarkerUtils.getStringVariable(Environment.getCurrentEnvironment(), TemplateUtils.TemplateVariable_Token);
if (url.contains("?")) {
return url + "&" + tokenName + "=" + token;
}
return url + "?" + token + "=" + token;
}
public static String appendTokenParameter(String url) {
if (StringUtils.isEmpty(url)) {
return url;
}
SaTokenConfig config = StpAdminUtil.getStpLogic().getConfigOrGlobal();
String token = StpAdminUtil.getTokenValue();
if (StringUtils.isNotEmpty(config.getTokenPrefix())) {
token = config.getTokenPrefix() + " " + token;
}
if (url.contains("?")) {
return url + "&" + config.getTokenName() + "=" + token;
}
return url + "?" + config.getTokenName() + "=" + token;
}
/**
* 页面区块静态文件相对路径
*

View File

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

View File

@ -1,9 +1,9 @@
package com.chestnut.customform.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.chestnut.common.utils.ConvertUtils;
import com.chestnut.xmodel.core.BaseModelData;
import lombok.Getter;
import lombok.Setter;
@ -52,4 +52,17 @@ public class CmsCustomFormData extends BaseModelData {
* 创建时间
*/
private LocalDateTime createTime;
@Override
public void setFieldValue(String fieldName, Object fieldValue) {
switch(fieldName) {
case "data_id" -> this.setDataId(ConvertUtils.toLong(fieldValue));
case "model_id" -> this.setModelId(ConvertUtils.toLong(fieldValue));
case "site_id" -> this.setSiteId(ConvertUtils.toLong(fieldValue));
case "client_ip" -> this.setClientIp(ConvertUtils.toStr(fieldValue));
case "uuid" -> this.setUuid(ConvertUtils.toStr(fieldValue));
case "create_time" -> this.setCreateTime(ConvertUtils.toLocalDateTime(fieldValue));
default -> super.setFieldValue(fieldName, fieldValue);
}
}
}

View File

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

View File

@ -1,6 +1,7 @@
package com.chestnut.exmodel.domain;
import com.baomidou.mybatisplus.annotation.TableName;
import com.chestnut.common.utils.ConvertUtils;
import com.chestnut.xmodel.core.BaseModelData;
import lombok.Getter;
import lombok.Setter;
@ -29,4 +30,14 @@ public class CmsExtendModelData extends BaseModelData {
* 关联元数据模型ID联合主键
*/
private Long modelId;
@Override
public void setFieldValue(String fieldName, Object fieldValue) {
switch(fieldName) {
case "data_id" -> this.setDataId(ConvertUtils.toLong(fieldValue));
case "data_type" -> this.setDataType(ConvertUtils.toStr(fieldValue));
case "model_id" -> this.setModelId(ConvertUtils.toLong(fieldValue));
default -> super.setFieldValue(fieldName, fieldValue);
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,95 @@
package com.chestnut.link;
import com.chestnut.common.async.AsyncTaskManager;
import com.chestnut.common.utils.IdUtils;
import com.chestnut.common.utils.JacksonUtils;
import com.chestnut.contentcore.core.ICoreDataHandler;
import com.chestnut.contentcore.core.SiteExportContext;
import com.chestnut.contentcore.core.SiteImportContext;
import com.chestnut.link.domain.CmsLink;
import com.chestnut.link.domain.CmsLinkGroup;
import com.chestnut.link.service.ILinkGroupService;
import com.chestnut.link.service.ILinkService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 友链内容核心数据处理器
*
* @author 兮玥
* @email 190785909@qq.com
*/
@Component
@RequiredArgsConstructor
public class LinkContentCoreHandler implements ICoreDataHandler {
private final ILinkGroupService linkGroupService;
private final ILinkService linkService;
@Override
public void onSiteExport(SiteExportContext context) {
// cms_link_group
int percent = AsyncTaskManager.getTaskProgressPercent();
AsyncTaskManager.setTaskProgressInfo( percent + (100 - percent) / 10,
"正在导出友情链接数据");
List<CmsLinkGroup> list = linkGroupService.lambdaQuery()
.eq(CmsLinkGroup::getSiteId, context.getSite().getSiteId())
.list();
context.saveData(CmsLinkGroup.TABLE_NAME, JacksonUtils.to(list));
List<CmsLink> linkList = linkService.lambdaQuery()
.eq(CmsLink::getSiteId, context.getSite().getSiteId())
.list();
context.saveData(CmsLink.TABLE_NAME, JacksonUtils.to(linkList));
}
@Override
public void onSiteImport(SiteImportContext context) {
int percent = AsyncTaskManager.getTaskProgressPercent();
AsyncTaskManager.setTaskProgressInfo( percent + (100 - percent) / 10,
"正在导入友情链接分组数据");
// cms_link_group
Map<Long, Long> linkGroupIdMap = new HashMap<>();
List<File> files = context.readDataFiles(CmsLinkGroup.TABLE_NAME);
files.forEach(f -> {
List<CmsLinkGroup> list = JacksonUtils.fromList(f, CmsLinkGroup.class);
for (CmsLinkGroup data : list) {
try {
Long oldLinkGroupId = data.getLinkGroupId();
data.setLinkGroupId(IdUtils.getSnowflakeId());
data.setSiteId(context.getSite().getSiteId());
data.createBy(context.getOperator());
linkGroupService.save(data);
linkGroupIdMap.put(oldLinkGroupId, data.getLinkGroupId());
} catch (Exception e) {
AsyncTaskManager.addErrMessage("导入友链分组数据失败:" + data.getName() + "[" + data.getCode() + "]");
e.printStackTrace();
}
}
});
// cms_link
files = context.readDataFiles(CmsLink.TABLE_NAME);
files.forEach(f -> {
List<CmsLink> list = JacksonUtils.fromList(f, CmsLink.class);
for (CmsLink data : list) {
try {
data.setLinkId(IdUtils.getSnowflakeId());
data.setSiteId(context.getSite().getSiteId());
data.setGroupId(linkGroupIdMap.get(data.getGroupId()));
data.createBy(context.getOperator());
data.setLogo(context.dealInternalUrl(data.getLogo()));
linkService.save(data);
} catch (Exception e) {
AsyncTaskManager.addErrMessage("导入友链数据失败:" + data.getName());
e.printStackTrace();
}
}
});
}
}

View File

@ -2,13 +2,8 @@ package com.chestnut.link.listener;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.chestnut.common.async.AsyncTaskManager;
import com.chestnut.common.utils.IdUtils;
import com.chestnut.common.utils.JacksonUtils;
import com.chestnut.common.utils.SortUtils;
import com.chestnut.contentcore.domain.CmsSite;
import com.chestnut.contentcore.listener.event.BeforeSiteDeleteEvent;
import com.chestnut.contentcore.listener.event.SiteThemeExportEvent;
import com.chestnut.contentcore.listener.event.SiteThemeImportEvent;
import com.chestnut.link.domain.CmsLink;
import com.chestnut.link.domain.CmsLinkGroup;
import com.chestnut.link.service.ILinkGroupService;
@ -17,11 +12,6 @@ import lombok.RequiredArgsConstructor;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
@Component
@RequiredArgsConstructor
public class LinkListener {
@ -58,45 +48,4 @@ public class LinkListener {
AsyncTaskManager.addErrMessage("删除友链数据错误:" + e.getMessage());
}
}
@EventListener
public void onSiteThemeImport(SiteThemeImportEvent event) throws IOException {
int percent = AsyncTaskManager.getTaskProgressPercent();
AsyncTaskManager.setTaskProgressInfo( percent + (100 - percent) / 10,
"正在导入友情链接分组数据");
// cms_link_group
File dataFile = new File(event.getDestDir() + "db/" + CmsLinkGroup.TABLE_NAME + ".json");
if (dataFile.exists()) {
List<CmsLinkGroup> list = JacksonUtils.fromList(dataFile, CmsLinkGroup.class);
for (CmsLinkGroup data : list) {
try {
CmsLinkGroup linkGroup = new CmsLinkGroup();
linkGroup.setSiteId(event.getSite().getSiteId());
linkGroup.setLinkGroupId(IdUtils.getSnowflakeId());
linkGroup.setName(data.getName());
linkGroup.setCode(data.getCode());
linkGroup.setSortFlag(SortUtils.getDefaultSortValue());
linkGroup.createBy(event.getOperator().getUsername());
linkGroupService.save(linkGroup);
} catch (Exception e) {
AsyncTaskManager.addErrMessage("导入友链分组数据添加失败:" + data.getName() + "|" + data.getCode() + " > " + e.getMessage());
}
}
}
}
@EventListener
public void onSiteThemeExport(SiteThemeExportEvent event) throws IOException {
// cms_link_group
int percent = AsyncTaskManager.getTaskProgressPercent();
AsyncTaskManager.setTaskProgressInfo( percent + (100 - percent) / 10,
"正在导出友情链接分组数据");
List<CmsLinkGroup> list = linkGroupService.lambdaQuery()
.eq(CmsLinkGroup::getSiteId, event.getSite().getSiteId())
.list();
String json = JacksonUtils.to(list);
event.getZipBuilder().add(json.getBytes(StandardCharsets.UTF_8))
.path("db/" + CmsLinkGroup.TABLE_NAME + ".json")
.save();
}
}

View File

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

View File

@ -0,0 +1,133 @@
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.common.utils.IdUtils;
import com.chestnut.common.utils.JacksonUtils;
import com.chestnut.contentcore.core.ICoreDataHandler;
import com.chestnut.contentcore.core.SiteExportContext;
import com.chestnut.contentcore.core.SiteImportContext;
import com.chestnut.media.domain.CmsAudio;
import com.chestnut.media.domain.CmsVideo;
import com.chestnut.media.service.IAudioService;
import com.chestnut.media.service.IVideoService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.List;
/**
* 广告页面部件内容核心数据处理器
*
* @author 兮玥
* @email 190785909@qq.com
*/
@Component
@RequiredArgsConstructor
public class MediaCoreDataHandler implements ICoreDataHandler {
private final IAudioService audioService;
private final IVideoService videoService;
@Override
public void onSiteExport(SiteExportContext context) {
// cms_audio
int percent = AsyncTaskManager.getTaskProgressPercent();
AsyncTaskManager.setTaskProgressInfo( percent + (100 - percent) / 10,
"正在导出音频内容数据");
int pageSize = 200;
long offset = 0;
int fileIndex = 1;
while (true) {
LambdaQueryWrapper<CmsAudio> q = new LambdaQueryWrapper<CmsAudio>()
.eq(CmsAudio::getSiteId, context.getSite().getSiteId())
.gt(CmsAudio::getAudioId, offset)
.orderByAsc(CmsAudio::getAudioId);
Page<CmsAudio> page = audioService.page(new Page<>(1, pageSize, false), q);
if (page.getRecords().size() > 0) {
context.saveData(CmsAudio.TABLE_NAME, JacksonUtils.to(page.getRecords()), fileIndex);
if (page.getRecords().size() < pageSize) {
break;
}
offset = page.getRecords().get(page.getRecords().size() - 1).getContentId();
fileIndex++;
} else {
break;
}
}
// cms_video
percent = AsyncTaskManager.getTaskProgressPercent();
AsyncTaskManager.setTaskProgressInfo( percent + (100 - percent) / 10,
"正在导出视频频内容数据");
offset = 0;
fileIndex = 1;
while (true) {
LambdaQueryWrapper<CmsVideo> q = new LambdaQueryWrapper<CmsVideo>()
.eq(CmsVideo::getSiteId, context.getSite().getSiteId())
.gt(CmsVideo::getVideoId, offset)
.orderByAsc(CmsVideo::getVideoId);
Page<CmsVideo> page = videoService.page(new Page<>(1, pageSize, false), q);
if (page.getRecords().size() > 0) {
context.saveData(CmsVideo.TABLE_NAME, JacksonUtils.to(page.getRecords()), fileIndex);
if (page.getRecords().size() < pageSize) {
break;
}
offset = page.getRecords().get(page.getRecords().size() - 1).getContentId();
fileIndex++;
} else {
break;
}
}
}
@Override
public void onSiteImport(SiteImportContext context) {
// cms_audio
int percent = AsyncTaskManager.getTaskProgressPercent();
AsyncTaskManager.setTaskProgressInfo( percent + (100 - percent) / 10,
"正在导入音频内容数据");
List<File> files = context.readDataFiles(CmsAudio.TABLE_NAME);
files.forEach(f -> {
List<CmsAudio> list = JacksonUtils.fromList(f, CmsAudio.class);
for (CmsAudio data : list) {
try {
data.setAudioId(IdUtils.getSnowflakeId());
data.setSiteId(context.getSite().getSiteId());
data.setContentId(context.getContentIdMap().get(data.getContentId()));
data.createBy(context.getOperator());
data.setPath(context.dealInternalUrl(data.getPath()));
audioService.save(data);
} catch (Exception e) {
AsyncTaskManager.addErrMessage("导入音频内容数据失败:" + data.getTitle()
+ "[" + data.getAudioId() + "]");
e.printStackTrace();
}
}
});
// cms_video
percent = AsyncTaskManager.getTaskProgressPercent();
AsyncTaskManager.setTaskProgressInfo( percent + (100 - percent) / 10,
"正在导入视频内容数据");
files = context.readDataFiles(CmsVideo.TABLE_NAME);
files.forEach(f -> {
List<CmsVideo> list = JacksonUtils.fromList(f, CmsVideo.class);
for (CmsVideo data : list) {
try {
data.setVideoId(IdUtils.getSnowflakeId());
data.setSiteId(context.getSite().getSiteId());
data.setContentId(context.getContentIdMap().get(data.getContentId()));
data.createBy(context.getOperator());
data.setPath(context.dealInternalUrl(data.getPath()));
videoService.save(data);
} catch (Exception e) {
AsyncTaskManager.addErrMessage("导入视频内容数据失败:" + data.getTitle()
+ "[" + data.getVideoId() + "]");
e.printStackTrace();
}
}
});
}
}

View File

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

View File

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

View File

@ -4,11 +4,11 @@ import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.ElasticsearchException;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.chestnut.cms.search.es.doc.ESContent;
import com.chestnut.cms.search.vo.ESContentVO;
import com.chestnut.common.domain.R;
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.common.utils.StringUtils;
@ -19,6 +19,7 @@ import com.chestnut.contentcore.service.impl.ContentDynamicDataService;
import com.chestnut.contentcore.util.InternalUrlUtils;
import com.chestnut.search.SearchConsts;
import com.chestnut.search.service.ISearchLogService;
import com.fasterxml.jackson.databind.node.ObjectNode;
import jakarta.validation.constraints.Min;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -48,6 +49,8 @@ public class SearchApiController extends BaseRestController {
private final ISearchLogService logService;
private final ContentDynamicDataService contentDynamicDataService;
@GetMapping("/query")
public R<?> selectDocumentList(
@RequestParam(value = "sid") Long siteId,
@ -139,5 +142,72 @@ public class SearchApiController extends BaseRestController {
return this.bindDataTable(list, Objects.isNull(sr.hits().total()) ? 0 : sr.hits().total().value());
}
private final ContentDynamicDataService contentDynamicDataService;
@GetMapping("/tag")
public R<?> selectDocumentByTag(
@RequestParam(value = "sid") Long siteId,
@RequestParam(value = "cid", required = false, defaultValue = "0") Long catalogId,
@RequestParam(value = "pp") String publishPipeCode,
@RequestParam(value = "q", required = false) @Length(max = 200) String query,
@RequestParam(value = "ct", required = false) String contentType,
@RequestParam(value = "page", required = false, defaultValue = "1") @Min(1) Integer page,
@RequestParam(value = "size", required = false, defaultValue = "10") @Min(1) Integer size,
@RequestParam(value = "preview", required = false, defaultValue = "false") Boolean preview) throws ElasticsearchException, IOException {
SearchResponse<ObjectNode> sr = esClient.search(s -> {
s.index(ESContent.INDEX_NAME) // 索引
.query(q ->
q.bool(b -> {
b.must(must -> must.term(tq -> tq.field("siteId").value(siteId)));
if (IdUtils.validate(catalogId)) {
b.must(must -> must.term(tq -> tq.field("catalogId").value(catalogId)));
}
if (StringUtils.isNotEmpty(contentType)) {
b.must(must -> must.term(tq -> tq.field("contentType").value(contentType)));
}
if (StringUtils.isNotEmpty(query)) {
String[] tags = query.split("\\s+");
b.must(should -> {
for (String tag : tags) {
should.constantScore(cs ->
cs.boost(1F).filter(f ->
f.match(m ->
m.field("tags").query(tag))));
}
return should;
});
}
return b;
})
);
s.sort(sort -> sort.field(f -> f.field("_score").order(SortOrder.Desc)));
s.sort(sort -> sort.field(f -> f.field("publishDate").order(SortOrder.Desc))); // 排序: _score:desc + publishDate:desc
s.from((page - 1) * size).size(size); // 分页0开始
return s;
}, ObjectNode.class);
List<ESContentVO> list = sr.hits().hits().stream().map(hit -> {
ObjectNode source = hit.source();
ESContentVO vo = JacksonUtils.getObjectMapper().convertValue(source, ESContentVO.class);
vo.setHitScore(hit.score());
vo.setPublishDateInstance(LocalDateTime.ofEpochSecond(vo.getPublishDate(), 0, ZoneOffset.UTC));
vo.setCreateTimeInstance(LocalDateTime.ofEpochSecond(vo.getCreateTime(), 0, ZoneOffset.UTC));
CmsCatalog catalog = this.catalogService.getCatalog(vo.getCatalogId());
if (Objects.nonNull(catalog)) {
vo.setCatalogName(catalog.getName());
}
vo.setLink(InternalUrlUtils.getActualUrl(vo.getLink(), publishPipeCode, preview));
vo.setLogo(InternalUrlUtils.getActualUrl(vo.getLogo(), publishPipeCode, preview));
return vo;
}).toList();
List<String> contentIds = list.stream().map(c -> c.getContentId().toString()).toList();
Map<Long, ContentDynamicDataVO> map = this.contentDynamicDataService.getContentDynamicDataList(contentIds)
.stream().collect(Collectors.toMap(ContentDynamicDataVO::getContentId, i -> i));
list.forEach(c -> {
ContentDynamicDataVO cdd = map.get(c.getContentId());
c.setViewCount(cdd.getViews());
c.setFavoriteCount(cdd.getFavorites());
c.setLikeCount(cdd.getLikes());
c.setCommentCount(cdd.getComments());
});
return this.bindDataTable(list, Objects.isNull(sr.hits().total()) ? 0 : sr.hits().total().value());
}
}

View File

@ -5,6 +5,7 @@ import com.chestnut.common.staticize.core.TemplateContext;
import com.chestnut.common.utils.ServletUtils;
import com.chestnut.common.utils.StringUtils;
import com.chestnut.contentcore.core.IDynamicPageType;
import com.chestnut.contentcore.util.TemplateUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.stereotype.Component;
@ -29,6 +30,7 @@ public class SearchDynamicPageType implements IDynamicPageType {
REQUEST_ARG_PUBLISHPIPE_CODE,
REQUEST_ARG_PREVIEW,
new RequestArg("q", "搜索词", RequestArgType.Parameter, false, null),
new RequestArg("cid", "栏目ID", RequestArgType.Parameter, false, null),
new RequestArg("ot", "是否只搜索标题", RequestArgType.Parameter, false, "false"),
new RequestArg("ct", "内容类型", RequestArgType.Parameter, false, null),
new RequestArg("page", "当前页码", RequestArgType.Parameter, false, "1")
@ -74,6 +76,13 @@ public class SearchDynamicPageType implements IDynamicPageType {
if (StringUtils.isNotEmpty(contentType)) {
link += "&ct=" + contentType;
}
String catalogId = parameters.get("cid");
if (StringUtils.isNotEmpty(catalogId)) {
link += "&cid=" + catalogId;
}
if (templateContext.isPreview()) {
link = TemplateUtils.appendTokenParameter(link);
}
templateContext.setPageIndex(MapUtils.getIntValue(parameters, "page", 1));
templateContext.setFirstFileName(link);
templateContext.setOtherFileName(link + "&page=" + TemplateContext.PlaceHolder_PageNo);

View File

@ -14,6 +14,7 @@ import com.chestnut.cms.search.properties.EnableIndexProperty;
import com.chestnut.common.async.AsyncTask;
import com.chestnut.common.async.AsyncTaskManager;
import com.chestnut.common.utils.Assert;
import com.chestnut.common.utils.DateUtils;
import com.chestnut.contentcore.core.IContent;
import com.chestnut.contentcore.core.IContentType;
import com.chestnut.contentcore.core.impl.InternalDataType_Content;
@ -35,6 +36,9 @@ import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.HashMap;
@ -176,6 +180,7 @@ public class ContentIndexService implements CommandLineRunner {
LambdaQueryWrapper<CmsContent> q = new LambdaQueryWrapper<CmsContent>()
.ne(CmsContent::getCopyType, ContentCopyType.Mapping)
.eq(CmsContent::getStatus, ContentStatus.PUBLISHED)
.ne(CmsContent::getLinkFlag, YesOrNo.YES)
.eq(!includeChild, CmsContent::getCatalogId, catalog.getCatalogId())
.likeRight(includeChild, CmsContent::getCatalogAncestors, catalog.getAncestors());
long total = this.contentService.count(q);
@ -202,7 +207,8 @@ public class ContentIndexService implements CommandLineRunner {
// 先重建索引
recreateIndex(site);
List<CmsCatalog> catalogs = catalogService.list();
List<CmsCatalog> catalogs = catalogService.lambdaQuery()
.eq(CmsCatalog::getSiteId, site.getSiteId()).list();
for (CmsCatalog catalog : catalogs) {
rebuildCatalog(catalog, false);
}
@ -249,7 +255,15 @@ public class ContentIndexService implements CommandLineRunner {
data.put("fullText", content.getFullText());
// 扩展模型数据
this.extendModelService.getModelData(content.getContentEntity()).forEach(fd -> {
data.put(fd.getFieldName(), fd.getValue());
if (fd.getValue() instanceof LocalDateTime date) {
data.put(fd.getFieldName(), date.toInstant(ZoneOffset.UTC).toEpochMilli());
} else if (fd.getValue() instanceof LocalDate date) {
data.put(fd.getFieldName(), date.atStartOfDay().toInstant(ZoneOffset.UTC).toEpochMilli());
} else if (fd.getValue() instanceof Instant date) {
data.put(fd.getFieldName(), date.toEpochMilli());
} else {
data.put(fd.getFieldName(), fd.getValue());
}
});
return data;
}

View File

@ -9,14 +9,17 @@ import com.chestnut.common.staticize.FreeMarkerUtils;
import com.chestnut.common.staticize.enums.TagAttrDataType;
import com.chestnut.common.staticize.tag.AbstractListTag;
import com.chestnut.common.staticize.tag.TagAttr;
import com.chestnut.common.staticize.tag.TagAttrOption;
import com.chestnut.common.utils.IdUtils;
import com.chestnut.common.utils.JacksonUtils;
import com.chestnut.common.utils.StringUtils;
import com.chestnut.contentcore.domain.dto.ContentDTO;
import com.chestnut.exmodel.CmsExtendMetaModelType;
import com.chestnut.search.SearchConsts;
import com.fasterxml.jackson.databind.node.ObjectNode;
import freemarker.core.Environment;
import freemarker.template.TemplateException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.springframework.stereotype.Component;
@ -25,7 +28,9 @@ import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@Slf4j
@Component
@RequiredArgsConstructor
public class CmsSearchContentTag extends AbstractListTag {
@ -34,26 +39,34 @@ public class CmsSearchContentTag extends AbstractListTag {
public final static String NAME = "{FREEMARKER.TAG.NAME." + TAG_NAME + "}";
public final static String DESC = "{FREEMARKER.TAG.DESC." + TAG_NAME + "}";
private final static String ATTR_QUERY = "query";
private final static String ATTR_CATALOG_ID = "catalogid";
private final static String ATTR_CONTENT_TYPE = "contenttype";
private final static String ATTR_MODE = "mode";
private final ElasticsearchClient esClient;
@Override
public List<TagAttr> getTagAttrs() {
List<TagAttr> tagAttrs = super.getTagAttrs();
tagAttrs.add(new TagAttr("query", true, TagAttrDataType.STRING, "检索词"));
tagAttrs.add(new TagAttr("contenttype", false, TagAttrDataType.STRING, "内容类型"));
tagAttrs.add(new TagAttr(ATTR_QUERY, true, TagAttrDataType.STRING, "检索词"));
tagAttrs.add(new TagAttr(ATTR_CATALOG_ID, false, TagAttrDataType.STRING, "栏目ID"));
tagAttrs.add(new TagAttr(ATTR_CONTENT_TYPE, false, TagAttrDataType.STRING, "内容类型"));
tagAttrs.add(new TagAttr(ATTR_MODE, false, TagAttrDataType.STRING, "检索方式",
SearchMode.toTagAttrOptions(), SearchMode.FullText.name()));
return tagAttrs;
}
@Override
public TagPageData prepareData(Environment env, Map<String, String> attrs, boolean page, int size, int pageIndex) throws TemplateException {
long siteId = FreeMarkerUtils.evalLongVariable(env, "Site.siteId");
String query = MapUtils.getString(attrs, "query");
String mode = MapUtils.getString(attrs, ATTR_MODE, SearchMode.FullText.name());
String query = MapUtils.getString(attrs, ATTR_QUERY);
if (StringUtils.isEmpty(query)) {
throw new TemplateException("Tag attr `query` cannot be empty.", env);
}
String[] keywords = StringUtils.split(query, ",");
String contentType = MapUtils.getString(attrs, "contenttype");
String contentType = MapUtils.getString(attrs, ATTR_CONTENT_TYPE);
Long catalogId = MapUtils.getLong(attrs, ATTR_CATALOG_ID);
try {
SearchResponse<ObjectNode> sr = esClient.search(s -> {
s.index(ESContent.INDEX_NAME) // 索引
@ -63,18 +76,37 @@ public class CmsSearchContentTag extends AbstractListTag {
if (StringUtils.isNotEmpty(contentType)) {
b.must(must -> must.term(tq -> tq.field("contentType").value(contentType)));
}
b.must(should -> {
for (String keyword : keywords) {
should.constantScore(cs ->
cs.boost(1F).filter(f ->
f.match(m ->
m.field("tags").query(keyword))));
}
return should;
});
if (IdUtils.validate(catalogId)) {
b.must(must -> must.term(tq -> tq.field("catalogId").value(catalogId)));
}
if (SearchMode.isFullText(mode)) {
b.must(must -> must
.multiMatch(match -> match
.analyzer(SearchConsts.IKAnalyzeType_Smart)
.fields("title^10", "fullText^1")
.query(query)
)
);
} else {
b.must(should -> {
String[] keywords = StringUtils.split(query, ",");
for (String keyword : keywords) {
should.constantScore(cs ->
cs.boost(1F).filter(f ->
f.match(m ->
m.field("tags").query(keyword))));
}
return should;
});
}
return b;
})
);
if (SearchMode.isFullText(mode)) {
s.highlight(h ->
h.fields("title", f -> f.preTags("<font color='red'>").postTags("</font>"))
.fields("fullText", f -> f.preTags("<font color='red'>").postTags("</font>")));
}
s.sort(sort -> sort.field(f -> f.field("_score").order(SortOrder.Desc)));
s.sort(sort -> sort.field(f -> f.field("publishDate").order(SortOrder.Desc))); // 排序: _score:desc + publishDate:desc
s.source(source -> source.filter(f -> f.excludes("fullText"))); // 过滤字段
@ -87,12 +119,32 @@ public class CmsSearchContentTag extends AbstractListTag {
}, ObjectNode.class);
List<ESContentVO> list = sr.hits().hits().stream().map(hit -> {
ESContentVO vo = JacksonUtils.getObjectMapper().convertValue(hit.source(), ESContentVO.class);
Objects.requireNonNull(hit.source()).fields().forEachRemaining(e -> {
if (e.getKey().startsWith(CmsExtendMetaModelType.DATA_FIELD_PREFIX)) {
String field = e.getKey().substring(CmsExtendMetaModelType.DATA_FIELD_PREFIX.length());
String value = e.getValue().asText();
vo.getExtendData().put(field, value);
}
});
vo.setHitScore(hit.score());
vo.setPublishDateInstance(LocalDateTime.ofEpochSecond(vo.getPublishDate(), 0, ZoneOffset.UTC));
vo.setCreateTimeInstance(LocalDateTime.ofEpochSecond(vo.getCreateTime(), 0, ZoneOffset.UTC));
if (SearchMode.isFullText(mode)) {
hit.highlight().forEach((key, value) -> {
try {
if (key.equals("fullText")) {
vo.setFullText(StringUtils.join(value.toArray(String[]::new)));
} else if (key.equals("title")) {
vo.setTitle(StringUtils.join(value.toArray(String[]::new)));
}
} catch (Exception ex) {
log.warn("Search api row parse failed: ", ex);
}
});
}
return vo;
}).toList();
return TagPageData.of(list, page ? sr.hits().total().value() : list.size());
return TagPageData.of(list, page ? Objects.requireNonNull(sr.hits().total()).value() : list.size());
} catch (IOException e) {
throw new TemplateException(e, env);
}
@ -112,4 +164,32 @@ public class CmsSearchContentTag extends AbstractListTag {
public String getDescription() {
return DESC;
}
private enum SearchMode {
// 所有站点
FullText("全文检索"),
// 当前站点
Tag("标签检索,多个标签英文逗号隔开");
private final String desc;
SearchMode(String desc) {
this.desc = desc;
}
static boolean isFullText(String mode) {
return FullText.name().equalsIgnoreCase(mode);
}
static boolean isTag(String mode) {
return Tag.name().equalsIgnoreCase(mode);
}
static List<TagAttrOption> toTagAttrOptions() {
return List.of(
new TagAttrOption(FullText.name(), FullText.desc),
new TagAttrOption(Tag.name(), Tag.desc)
);
}
}
}

View File

@ -1,6 +1,8 @@
package com.chestnut.cms.search.vo;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import lombok.Getter;
import lombok.Setter;
@ -58,4 +60,6 @@ public class ESContentVO {
private Long favoriteCount;
private Long viewCount;
private Map<String, Object> extendData = new HashMap<>();
}

View File

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

View File

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

View File

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

View File

@ -35,7 +35,7 @@ public class CmsTagWordTag extends AbstractListTag {
@Override
public List<TagAttr> getTagAttrs() {
List<TagAttr> tagAttrs = super.getTagAttrs();
tagAttrs.add(new TagAttr("group", false, TagAttrDataType.STRING, "TAG词分组编码") );
tagAttrs.add(new TagAttr("group", true, TagAttrDataType.STRING, "TAG词分组编码") );
return tagAttrs;
}

View File

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

View File

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

View File

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

View File

@ -1,205 +1,215 @@
package com.chestnut.common.utils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeParseException;
import java.util.Objects;
import java.util.function.Supplier;
public class ConvertUtils {
public class ConvertUtils {
/**
* 字符串转整数基础类型
*
* @param str
* @return
* 对象Object转指定格式
*
* @param obj 对象
* @param nonNullSupplier 非空转换器
* @param nullSupplier 空值转换器
*/
public static int toInt(String str) {
return toInt(str, 0);
}
/**
* 字符串转整数基础类型
*
* @param str
* @return
*/
public static int toInt(String str, int defaultV) {
return NumberUtils.toInt(str, defaultV);
}
public static int toInt(Object obj) {
return toInt(obj, 0);
}
public static int toInt(Object obj, int defaultV) {
public static <T> T nonNullOrElse(Object obj, Supplier<T> nonNullSupplier, Supplier<T> nullSupplier) {
if (Objects.isNull(obj)) {
return defaultV;
return nullSupplier.get();
}
return toInt(obj.toString(), defaultV);
return nonNullSupplier.get();
}
public static Integer toInteger(String str) {
return toInteger(str, 0);
public static <T> T nonNull(Object obj, Supplier<T> nonNullSupplier) {
if (Objects.isNull(obj)) {
return null;
}
return nonNullSupplier.get();
}
/**
* 字符串转整数类型如果非数字则返回默认值或0
*
* @param str
* @param defaultV
* @return
* 转整数类型默认返回null
*/
public static Integer toInteger(String str, Integer defaultV) {
return Integer.valueOf(toInt(str, defaultV));
public static Integer toInteger(Object obj) {
return toInteger(obj, null);
}
/**
* 字符串转布尔基础类型
*
* @param str
* @return
* 转整数类型
*/
public static boolean toBoolean(String str) {
return toBoolean(str, false);
public static Integer toInteger(Object obj, Integer defaultV) {
return nonNullOrElse(obj, () -> {
try {
return Integer.valueOf(obj.toString());
} catch (NumberFormatException e) {
return defaultV;
}
}, () -> defaultV);
}
/**
* 字符串转布尔类型
* "true" || "1" -> true
* "false" || "0" -> false
*
* @param str
* @param defaultV
* @return
*/
public static Boolean toBoolean(String str, boolean defaultV) {
private static Boolean paseBoolean(String str, boolean defaultV) {
return switch(str) {
case "true" -> Boolean.TRUE;
case "false" -> Boolean.FALSE;
case "1" -> Boolean.TRUE;
case "0" -> Boolean.FALSE;
case "true", "1" -> Boolean.TRUE;
case "false", "0" -> Boolean.FALSE;
default -> defaultV;
};
}
public static Boolean toBoolean(Object obj, boolean defaultV) {
if (Objects.isNull(obj)) {
return defaultV;
}
return toBoolean(obj.toString(), defaultV);
/**
* 转布尔类型默认返回false
*/
public static Boolean toBoolean(Object obj) {
return toBoolean(obj, null);
}
public static long toLong(Object obj) {
return toLong(obj, 0);
public static Boolean toBoolean(Object obj, Boolean defaultV) {
return nonNullOrElse(obj, () -> paseBoolean(obj.toString(), defaultV), () -> defaultV);
}
/**
* 字符串转长整数类型如果非数字则返回默认值或0
*
* @param obj
* @param defaultV
* @return
* 转长整数类型默认返回null
*/
public static long toLong(Object obj, long defaultV) {
if (Objects.isNull(obj)) {
return defaultV;
}
return NumberUtils.toLong(obj.toString(), defaultV);
}
public static float toFloat(Object obj) {
return toFloat(obj, 0);
public static Long toLong(Object obj) {
return toLong(obj, null);
}
/**
* 字符串转浮点类型如果非数字则返回默认值或0
*
* @param obj
* @param defaultV
* @return
* 转长整数类型
*/
public static float toFloat(Object obj, float defaultV) {
if (Objects.isNull(obj)) {
return defaultV;
}
return NumberUtils.toFloat(obj.toString(), defaultV);
}
public static double toDouble(Object obj) {
return toDouble(obj, 0);
public static Long toLong(Object obj, Long defaultV) {
return nonNullOrElse(obj, () -> {
try {
return Long.parseLong(obj.toString());
} catch (final NumberFormatException nfe) {
return defaultV;
}
}, () -> defaultV);
}
/**
* 字符串转双精度浮点类型如果非数字则返回默认值或0
*
* @param obj
* @param defaultV
* @return
* 转浮点类型默认返回null
*/
public static double toDouble(Object obj, double defaultV) {
if (Objects.isNull(obj)) {
return defaultV;
}
return NumberUtils.toDouble(obj.toString(), defaultV);
}
public static byte toByte(Object obj) {
return toByte(obj, (byte) 0);
public static Float toFloat(Object obj) {
return toFloat(obj, null);
}
/**
* 字符串转字节类型如果非数字则返回默认值或0
*
* @param obj
* @param defaultV
* @return
* 转浮点类型
*/
public static byte toByte(Object obj, byte defaultV) {
if (Objects.isNull(obj)) {
return defaultV;
}
return NumberUtils.toByte(obj.toString(), defaultV);
}
public static short toShort(Object obj) {
return toShort(obj, (short) 0);
public static Float toFloat(Object obj, Float defaultV) {
return nonNullOrElse(obj, () -> {
try {
return Float.parseFloat(obj.toString());
} catch (final NumberFormatException nfe) {
return defaultV;
}
}, () -> defaultV);
}
/**
* 字符串转短整数类型如果非数字则返回默认值或0
*
* @param obj
* @param defaultV
* @return
* 转双精度浮点类型默认返回null
*/
public static short toShort(Object obj, short defaultV) {
if (Objects.isNull(obj)) {
return defaultV;
}
return NumberUtils.toShort(obj.toString(), defaultV);
public static Double toDouble(Object obj) {
return toDouble(obj, null);
}
/**
* 转双精度浮点类型
*/
public static Double toDouble(Object obj, Double defaultV) {
return nonNullOrElse(obj, () -> {
try {
return Double.parseDouble(obj.toString());
} catch (final NumberFormatException nfe) {
return defaultV;
}
}, () -> defaultV);
}
/**
* 转字节类型默认返回null
*/
public static Byte toByte(Object obj) {
return toByte(obj, null);
}
/**
* 转字节类型
*/
public static Byte toByte(Object obj, Byte defaultV) {
return nonNullOrElse(obj, () -> {
try {
return Byte.parseByte(obj.toString());
} catch (final NumberFormatException nfe) {
return defaultV;
}
}, () -> defaultV);
}
/**
* 转短整数类型默认值null
*/
public static Short toShort(Object obj) {
return toShort(obj, null);
}
/**
* 转短整数类型
*/
public static Short toShort(Object obj, Short defaultV) {
return nonNullOrElse(obj, () -> {
try {
return Short.parseShort(obj.toString());
} catch (final NumberFormatException nfe) {
return defaultV;
}
}, () -> defaultV);
}
/**
* 转BigDecimal默认BigDecimal.ZERO
*/
public static BigDecimal toBigDecimal(Object obj) {
return toBigDecimal(obj, BigDecimal.ZERO);
}
/**
* 转BigDecimal
*/
public static BigDecimal toBigDecimal(Object obj, BigDecimal defaultV) {
if (Objects.isNull(obj)) {
return BigDecimal.ZERO;
}
return NumberUtils.toScaledBigDecimal(obj.toString());
return nonNullOrElse(obj, () -> NumberUtils.toScaledBigDecimal(obj.toString()), () -> defaultV);
}
/**
* 字符串如果为null则返回默认值
*
* @param str
* @param defaultValue
* @return
*/
public static String toStr(Object str, String defaultValue) {
return Objects.isNull(str) ? defaultValue : str.toString();
public static String toStr(Object obj, String defaultValue) {
return Objects.isNull(obj) ? defaultValue : obj.toString();
}
public static String toStr(Object str) {
return toStr(str, null);
public static String toStr(Object obj) {
return toStr(obj, null);
}
public static LocalDateTime toLocalDateTime(Object obj) {
return toLocalDateTime(obj, null);
}
public static LocalDateTime toLocalDateTime(Object obj, LocalDateTime defaultV) {
return nonNullOrElse(obj, () -> {
try {
return LocalDateTime.parse(obj.toString());
} catch (DateTimeParseException e) {
return defaultV;
}
}, () -> defaultV);
}
}

View File

@ -1,17 +1,16 @@
package com.chestnut.common.utils;
import com.chestnut.common.config.properties.ChestnutProperties;
import com.github.yitter.contract.IdGeneratorOptions;
import com.github.yitter.idgen.YitIdHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import com.chestnut.common.config.properties.ChestnutProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.yitter.contract.IdGeneratorOptions;
import com.github.yitter.idgen.YitIdHelper;
/**
* ID生成器工具类
*
@ -74,7 +73,7 @@ public class IdUtils {
}
for (Iterator<Long> iterator = ids.iterator(); iterator.hasNext();) {
Long id = iterator.next();
if (id == null || id.longValue() <= 0) {
if (id == null || id <= 0) {
if (removeInvalidId) {
iterator.remove();
} else {
@ -90,6 +89,6 @@ public class IdUtils {
}
public static boolean validate(Long id) {
return id != null && id.longValue() > 0;
return id != null && id > 0;
}
}

View File

@ -1,25 +1,5 @@
package com.chestnut.common.utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.collections4.map.CaseInsensitiveMap;
import org.springframework.util.LinkedCaseInsensitiveMap;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import eu.bitwalker.useragentutils.UserAgent;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.ServletRequest;
@ -27,6 +7,20 @@ import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.apache.commons.collections4.map.CaseInsensitiveMap;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.io.*;
import java.net.InetAddress;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 客户端工具类
@ -282,7 +276,7 @@ public class ServletUtils {
* 获取Integer参数
*/
public static int getParameterToInt(ServletRequest request, String name, int defaultValue) {
return ConvertUtils.toInt(request.getParameter(name), defaultValue);
return ConvertUtils.toInteger(request.getParameter(name), defaultValue);
}
/**

View File

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

View File

@ -360,6 +360,7 @@ public class SqlBuilder {
this.checkSqlInjection(field);
if (first) {
first = false;
} else {
append(", ");
}
append(field);

View File

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

View File

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

View File

@ -1,26 +1,5 @@
package com.chestnut.common.log.restful;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import com.chestnut.common.domain.R;
import com.chestnut.common.log.ILogHandler;
import com.chestnut.common.log.ILogType;
@ -33,8 +12,21 @@ import com.chestnut.common.security.anno.Priv;
import com.chestnut.common.security.domain.LoginUser;
import com.chestnut.common.utils.JacksonUtils;
import com.chestnut.common.utils.ServletUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
/**
* Restful Controller 访问日志
@ -72,6 +64,9 @@ public class RestfulLogType implements ILogType {
try {
if (joinPoint.getSignature() instanceof MethodSignature ms) {
Priv priv = ms.getMethod().getAnnotation(Priv.class);
if (Objects.isNull(priv)) {
priv = ms.getMethod().getDeclaringClass().getAnnotation(Priv.class);
}
if (Objects.nonNull(priv)) {
String userType = priv.type();
IUserType ut = this.securityService.getUserType(userType);
@ -80,7 +75,6 @@ public class RestfulLogType implements ILogType {
}
}
} catch (Exception e) {
logger.error("Log.Restful.beforeProceed: ", e.getMessage());
e.printStackTrace();
}
}
@ -134,7 +128,6 @@ public class RestfulLogType implements ILogType {
}
});
} catch (Exception ex) {
logger.error("Log.Restful.afterProceed: ", e.getMessage());
ex.printStackTrace();
}
}
@ -181,9 +174,9 @@ public class RestfulLogType implements ILogType {
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
} else if (Collection.class.isAssignableFrom(clazz)) {
Collection<?> collection = (Collection<?>) o;
return collection.stream().anyMatch(v -> isIgnoreArgs(v));
return collection.stream().anyMatch(this::isIgnoreArgs);
} else if (Map.class.isAssignableFrom(clazz)) {
return ((Map<?, ?>) o).values().stream().anyMatch(v -> isIgnoreArgs(v));
return ((Map<?, ?>) o).values().stream().anyMatch(this::isIgnoreArgs);
}
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|| o instanceof BindingResult;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,16 @@
package com.chestnut.xmodel.core;
import com.chestnut.common.utils.ConvertUtils;
import com.chestnut.common.utils.StringUtils;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 元数据模型数据表基类提供多种常见类型字段作为扩展字段
@ -20,349 +27,221 @@ import java.time.LocalDateTime;
@Setter
public class BaseModelData {
private String ShortText1;
private String shortText1;
private String ShortText2;
private String shortText2;
private String ShortText3;
private String shortText3;
private String ShortText4;
private String shortText4;
private String ShortText5;
private String shortText5;
private String ShortText6;
private String shortText6;
private String ShortText7;
private String shortText7;
private String ShortText8;
private String shortText8;
private String ShortText9;
private String shortText9;
private String ShortText10;
private String shortText10;
private String ShortText11;
private String shortText11;
private String ShortText12;
private String shortText12;
private String ShortText13;
private String shortText13;
private String ShortText14;
private String shortText14;
private String ShortText15;
private String shortText15;
private String ShortText16;
private String shortText16;
private String ShortText17;
private String shortText17;
private String ShortText18;
private String shortText18;
private String ShortText19;
private String shortText19;
private String ShortText20;
private String shortText20;
private String ShortText21;
private String shortText21;
private String ShortText22;
private String shortText22;
private String ShortText23;
private String shortText23;
private String ShortText24;
private String shortText24;
private String ShortText25;
private String shortText25;
private String MediumText1;
private String mediumText1;
private String MediumText2;
private String mediumText2;
private String MediumText3;
private String mediumText3;
private String MediumText4;
private String mediumText4;
private String MediumText5;
private String mediumText5;
private String MediumText6;
private String mediumText6;
private String MediumText7;
private String mediumText7;
private String MediumText8;
private String mediumText8;
private String MediumText9;
private String mediumText9;
private String MediumText10;
private String mediumText10;
private String MediumText11;
private String mediumText11;
private String MediumText12;
private String mediumText12;
private String MediumText13;
private String mediumText13;
private String MediumText14;
private String mediumText14;
private String MediumText15;
private String mediumText15;
private String MediumText16;
private String mediumText16;
private String MediumText17;
private String mediumText17;
private String MediumText18;
private String mediumText18;
private String MediumText19;
private String mediumText19;
private String MediumText20;
private String mediumText20;
private String MediumText21;
private String mediumText21;
private String MediumText22;
private String mediumText22;
private String MediumText23;
private String mediumText23;
private String MediumText24;
private String mediumText24;
private String MediumText25;
private String mediumText25;
private String LargeText1;
private String largeText1;
private String LargeText2;
private String largeText2;
private String LargeText3;
private String largeText3;
private String LargeText4;
private String largeText4;
private String ClobText1;
private String clobText1;
private Long Long1;
private Long long1;
private Long Long2;
private Long long2;
private Long Long3;
private Long long3;
private Long Long4;
private Long long4;
private Long Long5;
private Long long5;
private Long Long6;
private Long long6;
private Long Long7;
private Long long7;
private Long Long8;
private Long long8;
private Long Long9;
private Long long9;
private Long Long10;
private Long long10;
private Double Double1;
private Double double1;
private Double Double2;
private Double double2;
private Double Double3;
private Double double3;
private Double Double4;
private Double double4;
private Double Double5;
private Double double5;
private Double Double6;
private Double double6;
private Double Double7;
private Double double7;
private Double Double8;
private Double double8;
private Double Double9;
private Double double9;
private Double Double10;
private Double double10;
private LocalDateTime Date1;
private LocalDateTime date1;
private LocalDateTime Date2;
private LocalDateTime date2;
private LocalDateTime Date3;
private LocalDateTime date3;
private LocalDateTime Date4;
private LocalDateTime date4;
private LocalDateTime Date5;
private LocalDateTime date5;
private LocalDateTime Date6;
private LocalDateTime date6;
private LocalDateTime Date7;
private LocalDateTime date7;
private LocalDateTime Date8;
private LocalDateTime date8;
private LocalDateTime Date9;
private LocalDateTime date9;
private LocalDateTime Date10;
private LocalDateTime date10;
public void setFieldValue(String fieldName, Object fieldValue) {
if ("short_text1".equals(fieldName)) {
this.setShortText1(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text2".equals(fieldName)) {
this.setShortText2(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text3".equals(fieldName)) {
this.setShortText3(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text4".equals(fieldName)) {
this.setShortText4(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text5".equals(fieldName)) {
this.setShortText5(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text6".equals(fieldName)) {
this.setShortText6(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text7".equals(fieldName)) {
this.setShortText7(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text8".equals(fieldName)) {
this.setShortText8(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text9".equals(fieldName)) {
this.setShortText9(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text10".equals(fieldName)) {
this.setShortText10(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text11".equals(fieldName)) {
this.setShortText11(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text12".equals(fieldName)) {
this.setShortText12(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text13".equals(fieldName)) {
this.setShortText13(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text14".equals(fieldName)) {
this.setShortText14(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text15".equals(fieldName)) {
this.setShortText15(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text16".equals(fieldName)) {
this.setShortText16(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text17".equals(fieldName)) {
this.setShortText17(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text18".equals(fieldName)) {
this.setShortText18(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text19".equals(fieldName)) {
this.setShortText19(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text20".equals(fieldName)) {
this.setShortText20(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text21".equals(fieldName)) {
this.setShortText21(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text22".equals(fieldName)) {
this.setShortText22(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text23".equals(fieldName)) {
this.setShortText23(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text24".equals(fieldName)) {
this.setShortText24(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text25".equals(fieldName)) {
this.setShortText25(fieldValue == null ? null : fieldValue.toString());
} else if ("short_text25".equals(fieldName)) {
this.setShortText25(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text1".equals(fieldName)) {
this.setMediumText1(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text2".equals(fieldName)) {
this.setMediumText2(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text3".equals(fieldName)) {
this.setMediumText3(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text4".equals(fieldName)) {
this.setMediumText4(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text5".equals(fieldName)) {
this.setMediumText5(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text6".equals(fieldName)) {
this.setMediumText6(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text7".equals(fieldName)) {
this.setMediumText7(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text8".equals(fieldName)) {
this.setMediumText8(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text9".equals(fieldName)) {
this.setMediumText9(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text10".equals(fieldName)) {
this.setMediumText10(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text11".equals(fieldName)) {
this.setMediumText11(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text12".equals(fieldName)) {
this.setMediumText12(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text13".equals(fieldName)) {
this.setMediumText13(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text14".equals(fieldName)) {
this.setMediumText14(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text15".equals(fieldName)) {
this.setMediumText15(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text16".equals(fieldName)) {
this.setMediumText16(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text17".equals(fieldName)) {
this.setMediumText17(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text18".equals(fieldName)) {
this.setMediumText18(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text19".equals(fieldName)) {
this.setMediumText19(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text20".equals(fieldName)) {
this.setMediumText20(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text21".equals(fieldName)) {
this.setMediumText21(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text22".equals(fieldName)) {
this.setMediumText22(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text23".equals(fieldName)) {
this.setMediumText23(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text24".equals(fieldName)) {
this.setMediumText24(fieldValue == null ? null : fieldValue.toString());
} else if ("medium_text25".equals(fieldName)) {
this.setMediumText25(fieldValue == null ? null : fieldValue.toString());
} else if ("large_text1".equals(fieldName)) {
this.setLargeText1(fieldValue == null ? null : fieldValue.toString());
} else if ("large_text2".equals(fieldName)) {
this.setLargeText2(fieldValue == null ? null : fieldValue.toString());
} else if ("large_text3".equals(fieldName)) {
this.setLargeText3(fieldValue == null ? null : fieldValue.toString());
} else if ("large_text4".equals(fieldName)) {
this.setLargeText4(fieldValue == null ? null : fieldValue.toString());
} else if ("clob_text1".equals(fieldName)) {
this.setClobText1(fieldValue == null ? null : fieldValue.toString());
} else if ("long1".equals(fieldName)) {
this.setLong1(fieldValue == null ? null : Long.valueOf(fieldValue.toString()));
} else if ("long2".equals(fieldName)) {
this.setLong2(fieldValue == null ? null : Long.valueOf(fieldValue.toString()));
} else if ("long3".equals(fieldName)) {
this.setLong3(fieldValue == null ? null : Long.valueOf(fieldValue.toString()));
} else if ("long4".equals(fieldName)) {
this.setLong4(fieldValue == null ? null : Long.valueOf(fieldValue.toString()));
} else if ("long5".equals(fieldName)) {
this.setLong5(fieldValue == null ? null : Long.valueOf(fieldValue.toString()));
} else if ("long6".equals(fieldName)) {
this.setLong6(fieldValue == null ? null : Long.valueOf(fieldValue.toString()));
} else if ("long7".equals(fieldName)) {
this.setLong7(fieldValue == null ? null : Long.valueOf(fieldValue.toString()));
} else if ("long8".equals(fieldName)) {
this.setLong8(fieldValue == null ? null : Long.valueOf(fieldValue.toString()));
} else if ("long9".equals(fieldName)) {
this.setLong9(fieldValue == null ? null : Long.valueOf(fieldValue.toString()));
} else if ("long10".equals(fieldName)) {
this.setLong10(fieldValue == null ? null : Long.valueOf(fieldValue.toString()));
} else if ("double1".equals(fieldName)) {
this.setDouble1(fieldValue == null ? null : Double.valueOf(fieldValue.toString()));
} else if ("double2".equals(fieldName)) {
this.setDouble2(fieldValue == null ? null : Double.valueOf(fieldValue.toString()));
} else if ("double3".equals(fieldName)) {
this.setDouble3(fieldValue == null ? null : Double.valueOf(fieldValue.toString()));
} else if ("double4".equals(fieldName)) {
this.setDouble4(fieldValue == null ? null : Double.valueOf(fieldValue.toString()));
} else if ("double5".equals(fieldName)) {
this.setDouble5(fieldValue == null ? null : Double.valueOf(fieldValue.toString()));
} else if ("double6".equals(fieldName)) {
this.setDouble6(fieldValue == null ? null : Double.valueOf(fieldValue.toString()));
} else if ("double7".equals(fieldName)) {
this.setDouble7(fieldValue == null ? null : Double.valueOf(fieldValue.toString()));
} else if ("double8".equals(fieldName)) {
this.setDouble8(fieldValue == null ? null : Double.valueOf(fieldValue.toString()));
} else if ("double9".equals(fieldName)) {
this.setDouble9(fieldValue == null ? null : Double.valueOf(fieldValue.toString()));
} else if ("double10".equals(fieldName)) {
this.setDouble10(fieldValue == null ? null : Double.valueOf(fieldValue.toString()));
} else if ("date1".equals(fieldName)) {
this.setDate1(fieldValue == null ? null : LocalDateTime.parse(fieldValue.toString()));
} else if ("date2".equals(fieldName)) {
this.setDate2(fieldValue == null ? null : LocalDateTime.parse(fieldValue.toString()));
} else if ("date3".equals(fieldName)) {
this.setDate3(fieldValue == null ? null : LocalDateTime.parse(fieldValue.toString()));
} else if ("date4".equals(fieldName)) {
this.setDate4(fieldValue == null ? null : LocalDateTime.parse(fieldValue.toString()));
} else if ("date5".equals(fieldName)) {
this.setDate5(fieldValue == null ? null : LocalDateTime.parse(fieldValue.toString()));
} else if ("date6".equals(fieldName)) {
this.setDate6(fieldValue == null ? null : LocalDateTime.parse(fieldValue.toString()));
} else if ("date7".equals(fieldName)) {
this.setDate7(fieldValue == null ? null : LocalDateTime.parse(fieldValue.toString()));
} else if ("date8".equals(fieldName)) {
this.setDate8(fieldValue == null ? null : LocalDateTime.parse(fieldValue.toString()));
} else if ("date9".equals(fieldName)) {
this.setDate9(fieldValue == null ? null : LocalDateTime.parse(fieldValue.toString()));
} else if ("date10".equals(fieldName)) {
this.setDate10(fieldValue == null ? null : LocalDateTime.parse(fieldValue.toString()));
public void setFieldValue(@NotNull String fieldName, Object fieldValue) {
switch(fieldName) {
case "short_text1", "short_text2", "short_text3", "short_text4", "short_text5", "short_text6", "short_text7", "short_text8", "short_text9", "short_text10", "short_text11", "short_text12", "short_text13", "short_text14", "short_text15", "short_text16", "short_text17", "short_text18", "short_text19", "short_text20", "short_text21", "short_text22", "short_text23", "short_text24", "short_text25", "medium_text1", "medium_text2", "medium_text3", "medium_text4", "medium_text5", "medium_text6", "medium_text7", "medium_text8", "medium_text9", "medium_text10", "medium_text11", "medium_text12", "medium_text13", "medium_text14", "medium_text15", "medium_text16", "medium_text17", "medium_text18", "medium_text19", "medium_text20", "medium_text21", "medium_text22", "medium_text23", "medium_text24", "medium_text25", "large_text1", "large_text2", "large_text3", "large_text4", "clob_text1" -> ConvertUtils.toStr(fieldValue);
case "long1", "long2", "long3", "long4", "long5", "long6", "long7", "long8", "long9", "long10" -> ConvertUtils.toLong(fieldValue);
case "double1", "double2", "double3", "double4", "double5", "double6", "double7", "double8", "double9", "double10" -> ConvertUtils.toDouble(fieldValue);
case "date1", "date2", "date3", "date4", "date5", "date6", "date7", "date8", "date9", "date10" -> ConvertUtils.toLocalDateTime(fieldValue);
default -> throw new RuntimeException("Unknown field name: " + fieldName);
}
}
public static void main(String[] args) {
List<String> stringFields = new ArrayList<>();
List<String> longFields = new ArrayList<>();
List<String> doubleFields = new ArrayList<>();
List<String> dateFields = new ArrayList<>();
Field[] declaredFields = BaseModelData.class.getDeclaredFields();
for (Field field : declaredFields) {
String fieldName = StringUtils.toUnderScoreCase(field.getName());
if (field.getType() == String.class) {
stringFields.add(fieldName);
} else if (field.getType() == Long.class) {
longFields.add(fieldName);
} else if (field.getType() == Double.class) {
doubleFields.add(fieldName);
} else if (field.getType() == LocalDateTime.class) {
dateFields.add(fieldName);
}
}
StringBuilder sb = new StringBuilder();
sb.append("switch(fieldName) {\n");
String stringFieldsStr = stringFields.stream().map(fn -> "\"" + fn + "\"").collect(Collectors.joining(", "));
sb.append("\tcase ").append(stringFieldsStr).append(" -> ConvertUtils.toStr(fieldValue);\n");
String longFieldsStr = longFields.stream().map(fn -> "\"" + fn + "\"").collect(Collectors.joining(", "));
sb.append("\tcase ").append(longFieldsStr).append(" -> ConvertUtils.toLong(fieldValue);\n");
String doubleFieldStr = doubleFields.stream().map(fn -> "\"" + fn + "\"").collect(Collectors.joining(", "));
sb.append("\tcase ").append(doubleFieldStr).append(" -> ConvertUtils.toDouble(fieldValue);\n");
String dateFieldStr = dateFields.stream().map(fn -> "\"" + fn + "\"").collect(Collectors.joining(", "));
sb.append("\tcase ").append(dateFieldStr).append(" -> ConvertUtils.toLocalDateTime(fieldValue);\n");
sb.append("\tdefault -> throw new RuntimeException(\"Unknown field name: \" + fieldName);\n");
sb.append("}");
System.out.println(sb);
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,15 @@
package com.chestnut.system.groovy;
import com.chestnut.common.utils.SpringUtils;
import com.chestnut.common.utils.StringUtils;
import groovy.lang.GroovyClassLoader;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.annotation.AnnotationUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigInteger;
@ -8,23 +18,11 @@ import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.annotation.AnnotationUtils;
import com.chestnut.common.utils.SpringUtils;
import com.chestnut.common.utils.StringUtils;
import groovy.lang.GroovyClassLoader;
import jakarta.annotation.Resource;
public class GroovyScriptFactory {
private static Logger logger = LoggerFactory.getLogger(GroovyScriptFactory.class);
private static final Logger logger = LoggerFactory.getLogger(GroovyScriptFactory.class);
private static GroovyScriptFactory glueFactory = new GroovyScriptFactory();
private static final GroovyScriptFactory glueFactory = new GroovyScriptFactory();
public static GroovyScriptFactory getInstance() {
return glueFactory;
@ -33,9 +31,9 @@ public class GroovyScriptFactory {
/**
* groovy class loader
*/
private GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
private final GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
private ConcurrentMap<String, Class<?>> CLASS_CACHE = new ConcurrentHashMap<>();
private final ConcurrentMap<String, Class<?>> CLASS_CACHE = new ConcurrentHashMap<>();
/**
* load new instance, prototype
@ -49,14 +47,12 @@ public class GroovyScriptFactory {
Class<?> clazz = getCodeSourceClass(scriptText);
if (clazz != null) {
Object instance = clazz.getDeclaredConstructor().newInstance();
if (instance != null) {
if (instance instanceof BaseGroovyScript groovyScript) {
this.injectService(instance);
return groovyScript;
} else {
throw new IllegalArgumentException(
"Cannot convert from instance[" + instance.getClass() + "] to BaseGroovyScript");
}
if (instance instanceof BaseGroovyScript groovyScript) {
this.injectService(instance);
return groovyScript;
} else {
throw new IllegalArgumentException(
"Cannot convert from instance[" + instance.getClass() + "] to BaseGroovyScript");
}
}
}
@ -121,9 +117,7 @@ public class GroovyScriptFactory {
field.setAccessible(true);
try {
field.set(instance, fieldBean);
} catch (IllegalArgumentException e) {
logger.error(e.getMessage(), e);
} catch (IllegalAccessException e) {
} catch (IllegalArgumentException | IllegalAccessException e) {
logger.error(e.getMessage(), e);
}
}

View File

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

View File

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

View File

@ -50,6 +50,7 @@ public class TagWordController extends BaseRestController {
PageRequest pr = this.getPageRequest();
Page<TagWord> page = this.tagWordService.lambdaQuery().eq(TagWord::getGroupId, groupId)
.like(StringUtils.isNotEmpty(query), TagWord::getWord, query)
.orderByAsc(TagWord::getSortFlag)
.page(new Page<>(pr.getPageNumber(), pr.getPageSize(), true));
return this.bindDataTable(page);
}

View File

@ -1,14 +1,5 @@
package com.chestnut.word.service.impl;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chestnut.common.redis.RedisCache;
@ -19,8 +10,15 @@ import com.chestnut.word.domain.HotWordGroup;
import com.chestnut.word.mapper.HotWordGroupMapper;
import com.chestnut.word.mapper.HotWordMapper;
import com.chestnut.word.service.IHotWordService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
@ -36,11 +34,13 @@ public class HotWordServiceImpl extends ServiceImpl<HotWordMapper, HotWord> impl
public Map<String, HotWordCache> getHotWords(String groupCode) {
return this.redisCache.getCacheObject(CACHE_PREFIX + groupCode, () -> {
Optional<HotWordGroup> groupOpt = new LambdaQueryChainWrapper<>(this.hotWordGroupMapper).eq(HotWordGroup::getCode, groupCode).oneOpt();
if (!groupOpt.isPresent()) {
return null;
}
return this.lambdaQuery().eq(HotWord::getGroupId, groupOpt.get().getGroupId()).list().stream().collect(
Collectors.toMap(HotWord::getWord, w -> new HotWordCache(w.getWord(), w.getUrl(), w.getUrlTarget())));
return groupOpt.map(hotWordGroup -> this.lambdaQuery()
.eq(HotWord::getGroupId, hotWordGroup.getGroupId())
.list().stream()
.collect(Collectors.toMap(HotWord::getWord, w ->
new HotWordCache(w.getWord(), w.getUrl(), w.getUrlTarget())
))
).orElseGet(Map::of);
});
}

View File

@ -1,10 +1,5 @@
package com.chestnut.word.service.impl;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chestnut.common.exception.CommonErrorCode;
import com.chestnut.common.utils.Assert;
@ -13,6 +8,10 @@ import com.chestnut.common.utils.SortUtils;
import com.chestnut.word.domain.TagWord;
import com.chestnut.word.mapper.TagWordMapper;
import com.chestnut.word.service.ITagWordService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class TagWordServiceImpl extends ServiceImpl<TagWordMapper, TagWord> implements ITagWordService {
@ -38,6 +37,7 @@ public class TagWordServiceImpl extends ServiceImpl<TagWordMapper, TagWord> impl
dbTagWord.setGroupId(tagWord.getGroupId());
dbTagWord.setWord(tagWord.getWord());
dbTagWord.setLogo(tagWord.getLogo());
dbTagWord.setSortFlag(tagWord.getSortFlag());
dbTagWord.setRemark(tagWord.getRemark());
dbTagWord.updateBy(tagWord.getUpdateBy());
this.updateById(tagWord);

View File

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

View File

@ -1,6 +1,6 @@
{
"name": "ChestnutCMS",
"version": "1.3.23",
"version": "1.3.24",
"description": "ChestnutCMS[栗子内容管理系统]",
"author": "",
"license": "Apache-2.0",

View File

@ -137,6 +137,7 @@ export default {
},
methods: {
loadModelFieldData() {
this.fieldList = []
getXModelFieldData(this.modelId, this.dataType, this.dataId).then(response => {
response.data.forEach(item => {
let field = {};

View File

@ -18,13 +18,13 @@
ref="tagInput"
:size="size"
v-model="inputValue"
@keyup.enter.native="handleInputConfirm"
@keyup.enter.native="handleEnterInputConfirm"
@blur="handleInputConfirm"
>
</el-input>
</div>
<el-button-group v-if="!inputVisible">
<el-button class="button-new-tag" icon="el-icon-plus" :size="size" @click="handleNewTag">{{ btnName }}</el-button>
<el-button ref="tagAddBtn" class="button-new-tag" icon="el-icon-plus" :size="size" @click="handleNewTag">{{ btnName }}</el-button>
<el-button v-if="select" class="button-new-tag" icon="el-icon-search" :size="size" @click="handleSelectTag">{{ $t('Common.Select') }}</el-button>
</el-button-group>
@ -96,17 +96,21 @@ export default {
this.$refs.tagInput.$refs.input.focus();
});
},
handleEnterInputConfirm() {
this.handleInputConfirm()
this.handleNewTag()
},
handleInputConfirm() {
if (this.tagList.indexOf(this.inputValue) > -1) {
this.$modal.msgWarning("Repeated!");
return;
}
let inputValue = this.inputValue;
if (inputValue) {
if (inputValue && inputValue.trim().length > 0) {
this.tagList.push(inputValue.trim());
}
this.inputVisible = false;
this.inputValue = '';
this.inputVisible = false;
},
handleSelectTag() {
this.showTagSelector = true;

View File

@ -1,6 +1,6 @@
<template>
<div class="catalog-extend-container">
<el-row class="mb12">
<el-row :gutter="10" class="mb12">
<el-col :span="1.5">
<el-button
plain

View File

@ -606,6 +606,12 @@ export default {
if (!this.form.title || this.form.title.length == 0) {
this.form.title = contents[0].title;
}
if (!this.form.shortTitle || this.form.shortTitle.length == 0) {
this.form.shortTitle = contents[0].shortTitle;
}
if (!this.form.subTitle || this.form.subTitle.length == 0) {
this.form.subTitle = contents[0].subTitle;
}
if (!this.form.author || this.form.author.length == 0) {
this.form.author = contents[0].author;
}
@ -621,6 +627,8 @@ export default {
if (!this.form.summary || this.form.summary.length == 0) {
this.form.summary = contents[0].summary;
}
this.showOtherTitle = 'Y' === this.form.showSubTitle || (this.form.shortTitle && this.form.shortTitle.length > 0)
|| (this.form.subTitle && this.form.subTitle.length > 0);
this.openContentSelector = false;
} else {
this.$modal.msgWarning(this.$t('Common.SelectFirst'));

View File

@ -55,6 +55,7 @@
<el-table-column type="selection" width="50" align="center" />
<el-table-column type="index" :label="$t('Common.RowNo')" align="center" width="50" />
<el-table-column :label="$t('WordMgr.TAG.TAGWord')" align="left" prop="word" />
<el-table-column :label="$t('Common.Sort')" align="center" prop="sortFlag" width="120"/>
<el-table-column :label="$t('WordMgr.TAG.TAGWordUseCount')" align="center" prop="useCount" width="120"/>
<el-table-column :label="$t('WordMgr.TAG.TAGWordHitCount')" align="center" prop="hitCount" width="120"/>
<el-table-column :label="$t('Common.CreateTime')" align="center" width="160">
@ -101,6 +102,9 @@
<el-form-item :label="$t('WordMgr.TAG.TAGWord')" prop="word">
<el-input v-model="form.word" />
</el-form-item>
<el-form-item :label="$t('Common.Sort')" prop="sortFlag">
<el-input-number v-model="form.sortFlag" />
</el-form-item>
<el-form-item :label="$t('Common.Remark')" prop="remark">
<el-input v-model="form.remark" />
</el-form-item>

View File

@ -35,7 +35,7 @@ module.exports = {
proxy: {
// detail: https://cli.vuejs.org/config/#devserver-proxy
[process.env.VUE_APP_BASE_API]: {
target: `http://localhost:8080`,
target: `http://localhost:9080`,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''

View File

@ -4,14 +4,14 @@
<groupId>com.chestnut</groupId>
<artifactId>chestnut</artifactId>
<version>1.3.23</version>
<version>1.3.24</version>
<packaging>pom</packaging>
<name>ChestnutCMS</name>
<description>栗子内容管理系统</description>
<properties>
<chestnut.version>1.3.23</chestnut.version>
<chestnut.version>1.3.24</chestnut.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>