mirror of
https://gitee.com/liweiyi/ChestnutCMS.git
synced 2025-12-06 16:38:24 +08:00
版本更新到 1.4.2
This commit is contained in:
parent
85cab07588
commit
01adc9f252
@ -1,4 +1,4 @@
|
||||
# ChestnutCMS v1.4.1
|
||||
# ChestnutCMS v1.4.2
|
||||
|
||||
### 系统简介
|
||||
|
||||
@ -20,6 +20,8 @@ ChestnutCMS是前后端分离的企业级内容管理系统。项目基于[RuoYi
|
||||
|
||||
游戏站演示地址:PC端:<http://game.1000mz.com> 移动端:<http://mgame.1000mz.com>
|
||||
|
||||
影视站演示地址:PC端:<http://movie.1000mz.com> 移动端:<http://movie.1000mz.com>
|
||||
|
||||
### 开发环境
|
||||
- OpenJDK 17
|
||||
- Maven 3.8+
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>chestnut</artifactId>
|
||||
<groupId>com.chestnut</groupId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@ -5,7 +5,7 @@ chestnut:
|
||||
# 代号
|
||||
alias: ChestnutCMS
|
||||
# 版本
|
||||
version: 1.4.1
|
||||
version: 1.4.2
|
||||
# 版权年份
|
||||
copyrightYear: 2022-2024
|
||||
system:
|
||||
|
||||
@ -5,7 +5,7 @@ chestnut:
|
||||
# 代号
|
||||
alias: ChestnutCMS
|
||||
# 版本
|
||||
version: 1.4.1
|
||||
version: 1.4.2
|
||||
# 版权年份
|
||||
copyrightYear: 2022-2024
|
||||
system:
|
||||
|
||||
@ -5,7 +5,7 @@ chestnut:
|
||||
# 代号
|
||||
alias: ChestnutCMS
|
||||
# 版本
|
||||
version: 1.4.1
|
||||
version: 1.4.2
|
||||
# 版权年份
|
||||
copyrightYear: 2022-2024
|
||||
system:
|
||||
|
||||
@ -1,3 +1,22 @@
|
||||
CREATE TABLE `cms_site_visit_log` (
|
||||
`log_id` bigint NOT NULL,
|
||||
`site_id` bigint NOT NULL,
|
||||
`catalog_id` bigint DEFAULT NULL,
|
||||
`content_id` bigint DEFAULT NULL,
|
||||
`host` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`uri` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`ip` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`address` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`referer` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`browser` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`user_agent` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`os` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`device_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`locale` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`evt_time` datetime NOT NULL,
|
||||
PRIMARY KEY (`log_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
CREATE TABLE `cms_dynamic_page` (
|
||||
`page_id` bigint NOT NULL COMMENT 'ID',
|
||||
`site_id` bigint NOT NULL COMMENT '站点ID',
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
CREATE TABLE `search_word` (
|
||||
`word_id` bigint NOT NULL COMMENT '主键ID',
|
||||
`word` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '搜索词',
|
||||
`search_total` bigint NOT NULL COMMENT '历史累计搜索次数',
|
||||
`top_flag` bigint NOT NULL COMMENT '置顶标识',
|
||||
`top_date` datetime DEFAULT NULL COMMENT '置顶结束时间',
|
||||
`source` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '来源标识',
|
||||
`create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '创建人',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '最后修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '最后修改时间',
|
||||
`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
|
||||
PRIMARY KEY (`word_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
CREATE TABLE `search_word_hour_stat` (
|
||||
`stat_id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`hour` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
|
||||
`word_id` bigint NOT NULL,
|
||||
`word` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
|
||||
`search_count` bigint NOT NULL,
|
||||
PRIMARY KEY (`stat_id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
# 菜单名称路由调整
|
||||
UPDATE sys_menu SET component = 'search/wordTab', path = 'searchWord' where menu_id = 2053;
|
||||
|
||||
ALTER TABLE cms_content ADD COLUMN prop1 VARCHAR(100);
|
||||
ALTER TABLE cms_content ADD COLUMN prop2 VARCHAR(100);
|
||||
ALTER TABLE cms_content ADD COLUMN prop3 VARCHAR(255);
|
||||
ALTER TABLE cms_content ADD COLUMN prop4 VARCHAR(255);
|
||||
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.chestnut</groupId>
|
||||
<artifactId>chestnut-cms</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chestnut-cms-advertisement</artifactId>
|
||||
|
||||
@ -77,8 +77,17 @@ public class AdLogController extends BaseRestController {
|
||||
}
|
||||
|
||||
@GetMapping("/chart")
|
||||
public R<?> getLineChartStatDatas(@RequestParam @Min(1) Long advertisementId, @RequestParam Date beginTime, @RequestParam Date endTime) {
|
||||
List<CmsAdHourStat> list = this.advHourStatMapper.selectHourStat(advertisementId, FORMAT.format(beginTime), FORMAT.format(endTime));
|
||||
public R<?> getLineChartStatDatas(
|
||||
@RequestParam @Min(1) Long advertisementId,
|
||||
@RequestParam Date beginTime,
|
||||
@RequestParam Date endTime
|
||||
) {
|
||||
LambdaQueryWrapper<CmsAdHourStat> q = new LambdaQueryWrapper<CmsAdHourStat>()
|
||||
.eq(CmsAdHourStat::getAdvertisementId, advertisementId)
|
||||
.gt(Objects.nonNull(beginTime), CmsAdHourStat::getHour, beginTime)
|
||||
.gt(Objects.nonNull(endTime), CmsAdHourStat::getHour, endTime)
|
||||
.orderByAsc(CmsAdHourStat::getHour);
|
||||
List<CmsAdHourStat> list = this.advHourStatMapper.selectList(q);
|
||||
if (!list.isEmpty()) {
|
||||
Map<String, String> map = this.advService.getAdvertisementMap();
|
||||
list.forEach(l -> l.setAdName(map.get(l.getAdvertisementId().toString())));
|
||||
|
||||
@ -25,18 +25,6 @@ import com.chestnut.advertisement.domain.CmsAdHourStat;
|
||||
|
||||
public interface CmsAdHourStatMapper extends BaseMapper<CmsAdHourStat> {
|
||||
|
||||
@Select("""
|
||||
<script>
|
||||
SELECT * FROM `cms_ad_hour_stat`
|
||||
WHERE advertisement_id = #{advertisementId}
|
||||
<if test='begin != null'> and hour >= #{begin} </if>
|
||||
<if test='end != null'> and hour <= #{end} </if>
|
||||
ORDER BY hour ASC
|
||||
</script>
|
||||
""")
|
||||
public List<CmsAdHourStat> selectHourStat(@Param("advertisementId") Long advertisementId,
|
||||
@Param("begin") String begin, @Param("end") String end);
|
||||
|
||||
@Select("""
|
||||
<script>
|
||||
SELECT advertisement_id, sum(click) click, sum(view) view FROM `cms_ad_hour_stat`
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.chestnut</groupId>
|
||||
<artifactId>chestnut-cms</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chestnut-cms-article</artifactId>
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
package com.chestnut.article.template.func;
|
||||
|
||||
import com.chestnut.article.ArticleUtils;
|
||||
import com.chestnut.article.service.IArticleService;
|
||||
import com.chestnut.common.staticize.FreeMarkerUtils;
|
||||
import com.chestnut.common.staticize.core.TemplateContext;
|
||||
import com.chestnut.common.staticize.func.AbstractFunc;
|
||||
@ -41,8 +40,6 @@ public class dealArticleBodyFunction extends AbstractFunc {
|
||||
|
||||
private static final String DESC = "{FREEMARKER.FUNC.DESC." + FUNC_NAME + "}";
|
||||
|
||||
private final IArticleService articleService;
|
||||
|
||||
@Override
|
||||
public String getFuncName() {
|
||||
return FUNC_NAME;
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.chestnut</groupId>
|
||||
<artifactId>chestnut-cms</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chestnut-cms-block</artifactId>
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.chestnut</groupId>
|
||||
<artifactId>chestnut-cms</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chestnut-cms-comment</artifactId>
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.chestnut</groupId>
|
||||
<artifactId>chestnut-cms</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chestnut-cms-contentcore</artifactId>
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
package com.chestnut.contentcore.controller;
|
||||
|
||||
import com.chestnut.common.domain.R;
|
||||
import com.chestnut.common.i18n.I18nUtils;
|
||||
import com.chestnut.common.security.anno.Priv;
|
||||
import com.chestnut.common.security.web.BaseRestController;
|
||||
import com.chestnut.common.staticize.StaticizeService;
|
||||
@ -24,6 +25,7 @@ import com.chestnut.common.utils.Assert;
|
||||
import com.chestnut.common.utils.ServletUtils;
|
||||
import com.chestnut.contentcore.core.IInternalDataType;
|
||||
import com.chestnut.contentcore.domain.CmsSite;
|
||||
import com.chestnut.contentcore.domain.vo.DynamicPageTypeVO;
|
||||
import com.chestnut.contentcore.exception.ContentCoreErrorCode;
|
||||
import com.chestnut.contentcore.service.ISiteService;
|
||||
import com.chestnut.contentcore.service.ITemplateService;
|
||||
@ -44,6 +46,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
@ -154,6 +157,11 @@ public class CoreController extends BaseRestController {
|
||||
@Priv(type = AdminUserType.TYPE)
|
||||
@GetMapping("/cms/dynamicPageTypes")
|
||||
public R<?> getDynamicPageTypes() {
|
||||
return R.ok(ContentCoreUtils.getDynamicPageTypes());
|
||||
List<DynamicPageTypeVO> list = ContentCoreUtils.getDynamicPageTypes().stream()
|
||||
.map(DynamicPageTypeVO::newInstance).toList();
|
||||
list.forEach( vo ->
|
||||
vo.setName(I18nUtils.get(vo.getName()))
|
||||
);
|
||||
return R.ok(list);
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,6 +100,7 @@ public class TemplateController extends BaseRestController {
|
||||
.filesize(t.getFilesize())
|
||||
.filesizeName(FileUtils.byteCountToDisplaySize(t.getFilesize()))
|
||||
.modifyTime(DateUtils.epochMilliToLocalDateTime(t.getModifyTime()))
|
||||
.remark(t.getRemark())
|
||||
.build()).toList();
|
||||
return this.bindDataTable(list, (int) page.getTotal());
|
||||
}
|
||||
|
||||
@ -97,9 +97,9 @@ public class SiteExportContext implements ISiteThemeContext {
|
||||
* @param dest 目标路径,项目资源根目录(resourceRoot)
|
||||
*/
|
||||
public void saveFile(File source, String dest) {
|
||||
dest = ExportDir + SiteDirPath + dest;
|
||||
File destFile = new File(SiteUtils.getSiteResourceRoot(site) + dest);
|
||||
try {
|
||||
dest = ExportDir + SiteDirPath + dest;
|
||||
if (source.isDirectory()) {
|
||||
FileUtils.copyDirectory(source, destFile);
|
||||
} else {
|
||||
|
||||
@ -289,6 +289,26 @@ public class CmsContent extends BaseEntityWithLogicDelete {
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private Map<String, Object> configProps;
|
||||
|
||||
/**
|
||||
* 备用字段1
|
||||
*/
|
||||
private String prop1;
|
||||
|
||||
/**
|
||||
* 备用字段2
|
||||
*/
|
||||
private String prop2;
|
||||
|
||||
/**
|
||||
* 备用字段3
|
||||
*/
|
||||
private String prop3;
|
||||
|
||||
/**
|
||||
* 备用字段4
|
||||
*/
|
||||
private String prop4;
|
||||
|
||||
public boolean isLock() {
|
||||
return YesOrNo.isYes(this.isLock);
|
||||
}
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chestnut.contentcore.domain.vo;
|
||||
|
||||
import com.chestnut.contentcore.core.IDynamicPageType;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class DynamicPageTypeVO {
|
||||
|
||||
private String type;
|
||||
|
||||
private String name;
|
||||
|
||||
private String desc;
|
||||
|
||||
private String requestPath;
|
||||
|
||||
private String publishPipeKey;
|
||||
|
||||
private List<IDynamicPageType.RequestArg> requestArgs;
|
||||
|
||||
public static DynamicPageTypeVO newInstance(IDynamicPageType dynamicPageType) {
|
||||
DynamicPageTypeVO vo = new DynamicPageTypeVO();
|
||||
vo.setType(dynamicPageType.getType());
|
||||
vo.setName(dynamicPageType.getName());
|
||||
vo.setDesc(dynamicPageType.getDesc());
|
||||
vo.setRequestPath(dynamicPageType.getRequestPath());
|
||||
vo.setPublishPipeKey(dynamicPageType.getPublishPipeKey());
|
||||
vo.setRequestArgs(dynamicPageType.getRequestArgs());
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
@ -54,4 +54,6 @@ public class TemplateListVO {
|
||||
* 模板文件最后变更时间
|
||||
*/
|
||||
private LocalDateTime modifyTime;
|
||||
|
||||
private String remark;
|
||||
}
|
||||
|
||||
@ -36,12 +36,12 @@ public interface ISiteService extends IService<CmsSite> {
|
||||
* @param siteId
|
||||
* @return
|
||||
*/
|
||||
public boolean checkSiteUnique(String siteName, String sitePath, Long siteId);
|
||||
boolean checkSiteUnique(String siteName, String sitePath, Long siteId);
|
||||
|
||||
/**
|
||||
* 获取当前站点,保存在token中
|
||||
*/
|
||||
public CmsSite getCurrentSite(HttpServletRequest request);
|
||||
CmsSite getCurrentSite(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* 获取站点数据
|
||||
@ -49,7 +49,7 @@ public interface ISiteService extends IService<CmsSite> {
|
||||
* @param siteId
|
||||
* @return
|
||||
*/
|
||||
public CmsSite getSite(Long siteId);
|
||||
CmsSite getSite(Long siteId);
|
||||
|
||||
/**
|
||||
* 新增站点数据
|
||||
@ -58,7 +58,7 @@ public interface ISiteService extends IService<CmsSite> {
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public CmsSite addSite(SiteDTO dto) throws IOException;
|
||||
CmsSite addSite(SiteDTO dto) throws IOException;
|
||||
|
||||
/**
|
||||
* 修改站点数据
|
||||
@ -67,7 +67,7 @@ public interface ISiteService extends IService<CmsSite> {
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public CmsSite saveSite(SiteDTO site) throws IOException;
|
||||
CmsSite saveSite(SiteDTO site) throws IOException;
|
||||
|
||||
/**
|
||||
* 删除站点数据
|
||||
@ -76,7 +76,7 @@ public interface ISiteService extends IService<CmsSite> {
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public void deleteSite(Long siteId) throws IOException;
|
||||
void deleteSite(Long siteId) throws IOException;
|
||||
|
||||
/**
|
||||
* 保存站点默认模板配置
|
||||
@ -84,7 +84,7 @@ public interface ISiteService extends IService<CmsSite> {
|
||||
* @param dto
|
||||
* @return
|
||||
*/
|
||||
public void saveSiteDefaultTemplate(SiteDefaultTemplateDTO dto);
|
||||
void saveSiteDefaultTemplate(SiteDefaultTemplateDTO dto);
|
||||
|
||||
/**
|
||||
* 清理站点缓存数据
|
||||
|
||||
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chestnut.contentcore.template.func;
|
||||
|
||||
import com.chestnut.common.staticize.FreeMarkerUtils;
|
||||
import com.chestnut.common.staticize.core.TemplateContext;
|
||||
import com.chestnut.common.staticize.func.AbstractFunc;
|
||||
import com.chestnut.common.utils.StringUtils;
|
||||
import com.chestnut.contentcore.service.ICatalogService;
|
||||
import freemarker.core.Environment;
|
||||
import freemarker.template.SimpleNumber;
|
||||
import freemarker.template.TemplateModelException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Freemarker模板自定义函数:获取内容分页链接
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ContentPageLinkFunction extends AbstractFunc {
|
||||
|
||||
private static final String FUNC_NAME = "contentPageLink";
|
||||
|
||||
private static final String DESC = "{FREEMARKER.FUNC.DESC." + FUNC_NAME + "}";
|
||||
|
||||
private final ICatalogService catalogService;
|
||||
|
||||
@Override
|
||||
public String getFuncName() {
|
||||
return FUNC_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return DESC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object exec0(Object... args) throws TemplateModelException {
|
||||
if (args.length == 0) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
String link = args[0].toString();
|
||||
if (args.length == 1) {
|
||||
return link;
|
||||
}
|
||||
int pageNumber = ((SimpleNumber) args[1]).getAsNumber().intValue();
|
||||
if(pageNumber <= 1) {
|
||||
return link;
|
||||
}
|
||||
TemplateContext context = FreeMarkerUtils.getTemplateContext(Environment.getCurrentEnvironment());
|
||||
if (context.isPreview()) {
|
||||
link += "&pi=" + pageNumber;
|
||||
} else {
|
||||
int dotIndex = link.lastIndexOf(StringUtils.DOT);
|
||||
link = link.substring(0, dotIndex) + "_" + pageNumber + link.substring(dotIndex);
|
||||
}
|
||||
return link;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FuncArg> getFuncArgs() {
|
||||
return List.of(new FuncArg("内容链接", FuncArgType.String, true, null),
|
||||
new FuncArg("页码", FuncArgType.Int, true, null));
|
||||
}
|
||||
}
|
||||
@ -16,6 +16,7 @@
|
||||
package com.chestnut.contentcore.template.func;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@ -58,6 +59,9 @@ public class InternalUrlFunction extends AbstractFunc {
|
||||
}
|
||||
TemplateContext context = FreeMarkerUtils.getTemplateContext(Environment.getCurrentEnvironment());
|
||||
SimpleScalar simpleScalar = (SimpleScalar) args[0];
|
||||
if (Objects.isNull(simpleScalar)) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
return InternalUrlUtils.getActualUrl(simpleScalar.getAsString(), context.getPublishPipeCode(), context.isPreview());
|
||||
}
|
||||
|
||||
|
||||
@ -129,7 +129,7 @@ public class CmsContentTag extends AbstractListTag {
|
||||
|
||||
TemplateContext context = FreeMarkerUtils.getTemplateContext(env);
|
||||
Page<CmsContent> pageResult = this.contentService.page(new Page<>(pageIndex, size, page), q);
|
||||
if (pageIndex > 1 & pageResult.getRecords().size() == 0) {
|
||||
if (pageIndex > 1 & pageResult.getRecords().isEmpty()) {
|
||||
throw new TemplateException("内容列表页码超出上限:" + pageIndex, env);
|
||||
}
|
||||
List<ContentDTO> list = new ArrayList<>();
|
||||
|
||||
@ -73,7 +73,7 @@ public class CmsSitePropertyTag extends AbstractListTag {
|
||||
.eq(StringUtils.isNotEmpty(code), CmsSiteProperty::getPropCode, code);
|
||||
q.apply(StringUtils.isNotEmpty(condition), condition);
|
||||
Page<CmsSiteProperty> pageResult = this.sitePropertyService.page(new Page<>(pageIndex, size, page), q);
|
||||
if (pageIndex > 1 & pageResult.getRecords().size() == 0) {
|
||||
if (pageIndex > 1 & pageResult.getRecords().isEmpty()) {
|
||||
throw new TemplateException("站点自定义属性数据列表页码超出上限:" + pageIndex, env);
|
||||
}
|
||||
return TagPageData.of(pageResult.getRecords(), pageResult.getTotal());
|
||||
|
||||
@ -106,6 +106,7 @@ FREEMARKER.FUNC.DESC.dynamicPageLink=动态页面链接获取函数,例如:$
|
||||
FREEMARKER.FUNC.DESC.dict=获取字典数据列表,例如:${dict('YesOrNo', 'Y')}
|
||||
FREEMARKER.FUNC.DESC.sysConfig=获取系统参数配置值,例如:${sysConfig('SiteApiUrl')}
|
||||
FREEMARKER.FUNC.DESC.listHtmlInternalUrl=获取html文本中的iurl列表,例如:${listHtmlInternalUrl(ArticleContent)}
|
||||
FREEMARKER.FUNC.DESC.contentPageLink=获取内容分页链接,例如:${contentPageLink(content.link, 2)}
|
||||
FREEMARKER.FUNC.DESC.videoPlayer=将html文本中的视频资源链接替换为<video>视频播放器
|
||||
FREEMARKER.FUNC.videoPlayer.Arg1.Name=Html文本内容
|
||||
FREEMARKER.FUNC.videoPlayer.Arg2.Name=视频宽度
|
||||
|
||||
@ -106,6 +106,7 @@ FREEMARKER.FUNC.DESC.dynamicPageLink=Use ${dynamicPageLink('Search')} in templat
|
||||
FREEMARKER.FUNC.DESC.dict=Use ${dict(content.linkFlag, 'YesOrNo')} in template to get the dict data label.
|
||||
FREEMARKER.FUNC.DESC.sysConfig=Use ${sysConfig('SiteApiUrl')} in template to get the system config value.
|
||||
FREEMARKER.FUNC.DESC.listHtmlInternalUrl=Use ${listHtmlInternalUrl(ArticleContent)} in template to get the iurl list.
|
||||
FREEMARKER.FUNC.DESC.contentPageLink=Use ${contentPageLink(content.link, 2)} in template to get the content page link.
|
||||
FREEMARKER.FUNC.DESC.videoPlayer=Replace html video resource link tag to video tag.
|
||||
FREEMARKER.FUNC.videoPlayer.Arg1.Name=Html content
|
||||
FREEMARKER.FUNC.videoPlayer.Arg2.Name=Width
|
||||
|
||||
@ -106,6 +106,7 @@ FREEMARKER.FUNC.DESC.dynamicPageLink=動態頁面連結獲取函數,例如:$
|
||||
FREEMARKER.FUNC.DESC.dict=獲取字典數據列表,例如:${dict('YesOrNo', 'Y')}
|
||||
FREEMARKER.FUNC.DESC.sysConfig=獲取系統參數配置值,例如:${sysConfig('SiteApiUrl')}
|
||||
FREEMARKER.FUNC.DESC.listHtmlInternalUrl=獲取html文本中的iurl列表,例如:${listHtmlInternalUrl(ArticleContent)}
|
||||
FREEMARKER.FUNC.DESC.contentPageLink=獲取內容分頁鏈接,例如:${contentPageLink(content.link, 2)}
|
||||
FREEMARKER.FUNC.DESC.videoPlayer=將html文本中的視頻資源連結替換為<video>視頻播放器
|
||||
FREEMARKER.FUNC.videoPlayer.Arg1.Name=Html文本內容
|
||||
FREEMARKER.FUNC.videoPlayer.Arg2.Name=視頻寬度
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.chestnut</groupId>
|
||||
<artifactId>chestnut-cms</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chestnut-cms-customform</artifactId>
|
||||
|
||||
@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chestnut.customform;
|
||||
|
||||
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.common.utils.StringUtils;
|
||||
import com.chestnut.contentcore.core.ICoreDataHandler;
|
||||
import com.chestnut.contentcore.core.SiteExportContext;
|
||||
import com.chestnut.contentcore.core.SiteImportContext;
|
||||
import com.chestnut.contentcore.util.InternalUrlUtils;
|
||||
import com.chestnut.customform.domain.CmsCustomForm;
|
||||
import com.chestnut.customform.domain.CmsCustomFormData;
|
||||
import com.chestnut.customform.mapper.CustomFormDataMapper;
|
||||
import com.chestnut.customform.service.ICustomFormService;
|
||||
import com.chestnut.exmodel.MetaControlType_CmsImage;
|
||||
import com.chestnut.exmodel.MetaControlType_UEditor;
|
||||
import com.chestnut.xmodel.core.MetaModel;
|
||||
import com.chestnut.xmodel.domain.XModel;
|
||||
import com.chestnut.xmodel.domain.XModelField;
|
||||
import com.chestnut.xmodel.service.IModelFieldService;
|
||||
import com.chestnut.xmodel.service.IModelService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
/**
|
||||
* 自定义表单内容核心数据处理器
|
||||
*
|
||||
* @author 兮玥
|
||||
* @email 190785909@qq.com
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class CustomFormCoreDataHandler implements ICoreDataHandler {
|
||||
|
||||
private static final String XMODEL_TABLE_SUFFIX = "_cms_custom_form";
|
||||
|
||||
private final ICustomFormService customFormService;
|
||||
|
||||
private final CustomFormDataMapper customFormDataMapper;
|
||||
|
||||
private final IModelService modelService;
|
||||
|
||||
private final IModelFieldService modelFieldService;
|
||||
|
||||
@Override
|
||||
public void onSiteExport(SiteExportContext context) {
|
||||
AsyncTaskManager.setTaskTenPercentProgressInfo("正在导出自定义表单数据");
|
||||
// 自定义表单数据导出
|
||||
List<CmsCustomForm> customForms = customFormService.lambdaQuery()
|
||||
.eq(CmsCustomForm::getSiteId, context.getSite().getSiteId())
|
||||
.list();
|
||||
context.saveData(CmsCustomForm.TABLE_NAME, JacksonUtils.to(customForms));
|
||||
|
||||
// 自定义表单关联扩展模型
|
||||
List<Long> modelIds = customForms.stream().map(CmsCustomForm::getModelId).toList();
|
||||
if (modelIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<XModel> modelList = this.modelService.lambdaQuery().in(XModel::getModelId, modelIds).list();
|
||||
context.saveData(XModel.TABLE_NAME + XMODEL_TABLE_SUFFIX, JacksonUtils.to(modelList));
|
||||
// 扩展模型字段
|
||||
List<XModelField> fieldList = this.modelFieldService.lambdaQuery()
|
||||
.in(XModelField::getModelId, modelIds).list();
|
||||
context.saveData(XModelField.TABLE_NAME + XMODEL_TABLE_SUFFIX, JacksonUtils.to(fieldList));
|
||||
// 扩展模型数据
|
||||
int pageSize = 200;
|
||||
int fileIndex = 1;
|
||||
for (XModel model : modelList) {
|
||||
int pageIndex = 1;
|
||||
while (true) {
|
||||
LambdaQueryWrapper<CmsCustomFormData> q = new LambdaQueryWrapper<CmsCustomFormData>()
|
||||
.eq(CmsCustomFormData::getModelId, model.getModelId());
|
||||
Page<CmsCustomFormData> page = customFormDataMapper.selectPage(new Page<>(pageIndex, pageSize, false), q);
|
||||
if (page.getRecords().isEmpty()) {
|
||||
break;
|
||||
}
|
||||
pageIndex++;
|
||||
context.saveData(CmsCustomFormData.TABLE_NAME, JacksonUtils.to(page.getRecords()), fileIndex);
|
||||
fileIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSiteImport(SiteImportContext context) {
|
||||
// CmsCustomForm
|
||||
AsyncTaskManager.setTaskTenPercentProgressInfo("正在导入自定义表单");
|
||||
Map<Long, Long> customFormIdMapping = new HashMap<>();
|
||||
List<File> files = context.readDataFiles(CmsCustomForm.TABLE_NAME);
|
||||
files.forEach(f -> {
|
||||
List<CmsCustomForm> list = JacksonUtils.fromList(f, CmsCustomForm.class);
|
||||
for (CmsCustomForm data : list) {
|
||||
try {
|
||||
Long oldFormId = data.getFormId();
|
||||
data.setFormId(IdUtils.getSnowflakeId());
|
||||
data.setModelId(data.getFormId());
|
||||
data.setSiteId(context.getSite().getSiteId());
|
||||
data.createBy(context.getOperator());
|
||||
customFormService.save(data);
|
||||
customFormIdMapping.put(oldFormId, data.getFormId());
|
||||
} catch (Exception e) {
|
||||
AsyncTaskManager.addErrMessage("导入自定义表单失败:" + data.getName()
|
||||
+ "[" + data.getModelId() + "]");
|
||||
log.error("Import custom form failed: {}", data.getCode(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (files.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// XModel
|
||||
AsyncTaskManager.setTaskTenPercentProgressInfo("正在导入自定义表单元数据模型");
|
||||
files = context.readDataFiles(XModel.TABLE_NAME + XMODEL_TABLE_SUFFIX);
|
||||
files.forEach(f -> {
|
||||
List<XModel> list = JacksonUtils.fromList(f, XModel.class);
|
||||
for (XModel data : list) {
|
||||
try {
|
||||
data.setModelId(customFormIdMapping.get(data.getModelId()));
|
||||
data.setCode(data.getModelId().toString());
|
||||
data.setOwnerId(context.getSite().getSiteId().toString());
|
||||
data.createBy(context.getOperator());
|
||||
modelService.save(data);
|
||||
} catch (Exception e) {
|
||||
AsyncTaskManager.addErrMessage("导入自定义表单元数据模型失败:" + data.getName()
|
||||
+ "[" + data.getModelId() + "]");
|
||||
log.error("Import custom form xmodel failed: {}", data.getCode(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
// XModelField
|
||||
AsyncTaskManager.setTaskTenPercentProgressInfo("正在导入自定义表单模型字段");
|
||||
files = context.readDataFiles(XModelField.TABLE_NAME + XMODEL_TABLE_SUFFIX);
|
||||
files.forEach(f -> {
|
||||
List<XModelField> list = JacksonUtils.fromList(f, XModelField.class);
|
||||
for (XModelField data : list) {
|
||||
try {
|
||||
data.setFieldId(IdUtils.getSnowflakeId());
|
||||
data.setModelId(customFormIdMapping.get(data.getModelId()));
|
||||
data.createBy(context.getOperator());
|
||||
modelFieldService.save(data);
|
||||
} catch (Exception e) {
|
||||
AsyncTaskManager.addErrMessage("导入自定义表单模型字段失败:" + data.getName()
|
||||
+ "[" + data.getFieldId() + "]");
|
||||
log.error("Import custom form xmodel field failed: {}", data.getCode(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
// CmsCustomFormData
|
||||
AsyncTaskManager.setTaskTenPercentProgressInfo("正在导入自定义表单数据");
|
||||
files = context.readDataFiles(CmsCustomFormData.TABLE_NAME);
|
||||
files.forEach(f -> {
|
||||
List<CmsCustomFormData> list = JacksonUtils.fromList(f, CmsCustomFormData.class);
|
||||
for (CmsCustomFormData data : list) {
|
||||
try {
|
||||
data.setDataId(IdUtils.getSnowflakeId());
|
||||
data.setModelId(customFormIdMapping.get(data.getModelId()));
|
||||
// 处理图片和富文本内部链接
|
||||
MetaModel model = modelService.getMetaModel(data.getModelId());
|
||||
model.getFields().forEach(field -> {
|
||||
if (MetaControlType_CmsImage.TYPE.equals(field.getControlType())) {
|
||||
String fieldValue = data.getStringFieldValue(field.getFieldName());
|
||||
data.setFieldValue(field.getFieldName(), context.dealInternalUrl(fieldValue));
|
||||
} else if (MetaControlType_UEditor.TYPE.equals(field.getControlType())) {
|
||||
String fieldValue = data.getStringFieldValue(field.getFieldName());
|
||||
if (StringUtils.isNotEmpty(fieldValue)) {
|
||||
// 替换正文内部资源地址
|
||||
StringBuilder html = new StringBuilder();
|
||||
int index = 0;
|
||||
Matcher matcher = InternalUrlUtils.InternalUrlTagPattern.matcher(fieldValue);
|
||||
while (matcher.find()) {
|
||||
String tagStr = matcher.group();
|
||||
String iurl = matcher.group(1);
|
||||
String newIurl = context.dealInternalUrl(iurl);
|
||||
tagStr = StringUtils.replaceEx(tagStr, iurl, newIurl);
|
||||
html.append(fieldValue, index, matcher.start()).append(tagStr);
|
||||
index = matcher.end();
|
||||
}
|
||||
html.append(fieldValue.substring(index));
|
||||
data.setFieldValue(field.getFieldName(), html.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
customFormDataMapper.insert(data);
|
||||
} catch (Exception e) {
|
||||
AsyncTaskManager.addErrMessage("导入自定义及表单数据失败:" + data.getModelId() + "-" + data.getDataId());
|
||||
log.error("Import custom form data failed: {} - {}", data.getModelId(), data.getDataId(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -51,7 +51,7 @@ public class CmsCustomForm extends BaseEntity {
|
||||
private Long siteId;
|
||||
|
||||
/**
|
||||
* 关联元数据模型ID
|
||||
* 关联元数据模型ID(与主键form_id一致)
|
||||
*/
|
||||
private Long modelId;
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.chestnut</groupId>
|
||||
<artifactId>chestnut-cms</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chestnut-cms-dynamic</artifactId>
|
||||
|
||||
@ -18,6 +18,7 @@ package com.chestnut.cms.dynamic.controller;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.chestnut.cms.dynamic.core.IDynamicPageInitData;
|
||||
import com.chestnut.cms.dynamic.domain.CmsDynamicPage;
|
||||
import com.chestnut.cms.dynamic.domain.vo.DynamicPageInitDataTypeVO;
|
||||
import com.chestnut.cms.dynamic.service.IDynamicPageService;
|
||||
import com.chestnut.common.domain.R;
|
||||
import com.chestnut.common.exception.CommonErrorCode;
|
||||
@ -78,7 +79,9 @@ public class DynamicPageController extends BaseRestController {
|
||||
|
||||
@GetMapping("/init_data_types")
|
||||
public R<?> getInitDataTypes() {
|
||||
return R.ok(initDataTypes);
|
||||
List<DynamicPageInitDataTypeVO> list = initDataTypes.stream()
|
||||
.map(DynamicPageInitDataTypeVO::newInstance).toList();
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
@GetMapping("/{pageId}")
|
||||
|
||||
@ -52,7 +52,7 @@ public class DynamicPageFrontController extends BaseRestController {
|
||||
public void handleDynamicPageRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
Map<String, String> parameters = ServletUtils.getParameters();
|
||||
String requestURI = request.getRequestURI();
|
||||
System.out.println("dynamicPage: " + requestURI);
|
||||
|
||||
Long siteId = MapUtils.getLong(parameters, "sid", 0L);
|
||||
String publishPipeCode = parameters.get("pp");
|
||||
boolean preview = MapUtils.getBoolean(parameters, "preview", false);
|
||||
|
||||
@ -17,6 +17,8 @@ package com.chestnut.cms.dynamic.core;
|
||||
|
||||
import com.chestnut.common.staticize.core.TemplateContext;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 动态页面初始化数据接口
|
||||
*
|
||||
@ -27,7 +29,7 @@ public interface IDynamicPageInitData {
|
||||
|
||||
String BEAN_PREFIX = "DynamicPageInitData.";
|
||||
|
||||
void initTemplateData(TemplateContext context);
|
||||
void initTemplateData(TemplateContext context, Map<String, String> parameters);
|
||||
|
||||
String getType();
|
||||
|
||||
|
||||
@ -22,27 +22,31 @@ import com.chestnut.member.security.StpMemberUtil;
|
||||
import com.chestnut.member.util.MemberUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* MemberDynamicPageInitData
|
||||
*
|
||||
* @author 兮玥
|
||||
* @email 190785909@qq.com
|
||||
*/
|
||||
@Component
|
||||
@Component(IDynamicPageInitData.BEAN_PREFIX + MemberDynamicPageInitData.TYPE)
|
||||
public class MemberDynamicPageInitData implements IDynamicPageInitData {
|
||||
|
||||
public static final String TYPE = "Member";
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "Member";
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "登录会员";
|
||||
return "{DYNAMIC_PAGE_INIT_DATA." + TYPE + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initTemplateData(TemplateContext context) {
|
||||
public void initTemplateData(TemplateContext context, Map<String, String> parameters) {
|
||||
if (StpMemberUtil.isLogin()) {
|
||||
LoginUser loginUser = StpMemberUtil.getLoginUser();
|
||||
context.getVariables().put("Member", loginUser.getUser());
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chestnut.cms.dynamic.core.impl;
|
||||
|
||||
import com.chestnut.cms.dynamic.core.IDynamicPageInitData;
|
||||
import com.chestnut.common.staticize.core.TemplateContext;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* PaginationDynamicPageInitData
|
||||
*
|
||||
* @author 兮玥
|
||||
* @email 190785909@qq.com
|
||||
*/
|
||||
@Component(IDynamicPageInitData.BEAN_PREFIX + PaginationDynamicPageInitData.TYPE)
|
||||
public class PaginationDynamicPageInitData implements IDynamicPageInitData {
|
||||
|
||||
public static final String TYPE = "Pagination";
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "{DYNAMIC_PAGE_INIT_DATA." + TYPE + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initTemplateData(TemplateContext context, Map<String, String> parameters) {
|
||||
int pageIndex = MapUtils.getIntValue(parameters, "pi", 1);
|
||||
context.setPageIndex(pageIndex);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chestnut.cms.dynamic.domain.vo;
|
||||
|
||||
import com.chestnut.cms.dynamic.core.IDynamicPageInitData;
|
||||
import com.chestnut.common.i18n.I18nUtils;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 自定义动态页面初始化数据类型VO
|
||||
*
|
||||
* @author 兮玥
|
||||
* @email 190785909@qq.com
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class DynamicPageInitDataTypeVO {
|
||||
|
||||
/**
|
||||
* 类型,唯一标识
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
public static DynamicPageInitDataTypeVO newInstance(IDynamicPageInitData dynamicPageInitData) {
|
||||
DynamicPageInitDataTypeVO vo = new DynamicPageInitDataTypeVO();
|
||||
vo.setType(dynamicPageInitData.getType());
|
||||
vo.setName(I18nUtils.get(dynamicPageInitData.getName()));
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
@ -25,8 +25,6 @@ import java.util.Map;
|
||||
|
||||
public interface IDynamicPageService extends IService<CmsDynamicPage> {
|
||||
|
||||
String getDynamicPagePath(Long siteId, String code);
|
||||
|
||||
void addDynamicPage(CmsDynamicPage dynamicPage);
|
||||
|
||||
void saveDynamicPage(CmsDynamicPage dynamicPage);
|
||||
|
||||
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chestnut.cms.dynamic.service.impl;
|
||||
|
||||
import com.chestnut.cms.dynamic.core.IDynamicPageInitData;
|
||||
import com.chestnut.cms.dynamic.domain.CmsDynamicPage;
|
||||
import com.chestnut.common.redis.RedisCache;
|
||||
import com.chestnut.contentcore.config.CMSConfig;
|
||||
import com.chestnut.contentcore.core.IDynamicPageType;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class DynamicPageHelper {
|
||||
|
||||
private static final String CACHE_PREFIX = CMSConfig.CachePrefix + "dynamic_page:";
|
||||
|
||||
|
||||
private final RedisCache redisCache;
|
||||
|
||||
private final Map<String, IDynamicPageInitData> dynamicPageInitDataMap;
|
||||
|
||||
private final Map<String, IDynamicPageType> dynamicPageTypeMap;
|
||||
|
||||
public IDynamicPageInitData getDynamicPageInitData(String type) {
|
||||
return dynamicPageInitDataMap.get(IDynamicPageInitData.BEAN_PREFIX + type);
|
||||
}
|
||||
|
||||
public String getDynamicPagePath(Long siteId, String code) {
|
||||
IDynamicPageType dynamicPageType = this.dynamicPageTypeMap.get(IDynamicPageType.BEAN_PREFIX + code);
|
||||
if (Objects.nonNull(dynamicPageType)) {
|
||||
return dynamicPageType.getRequestPath();
|
||||
}
|
||||
CmsDynamicPage dynamicPage = getDynamicPage(siteId, code);
|
||||
if (Objects.nonNull(dynamicPage)) {
|
||||
return dynamicPage.getPath();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void clearCache(CmsDynamicPage dynamicPage) {
|
||||
this.redisCache.deleteObject(CACHE_PREFIX + dynamicPage.getSiteId() + ":" + dynamicPage.getPath());
|
||||
this.redisCache.deleteObject(CACHE_PREFIX + dynamicPage.getSiteId() + ":" + dynamicPage.getCode());
|
||||
}
|
||||
|
||||
public void updateCache(CmsDynamicPage dynamicPage) {
|
||||
this.redisCache.setCacheObject(CACHE_PREFIX + dynamicPage.getSiteId() + ":" + dynamicPage.getPath(), dynamicPage);
|
||||
this.redisCache.setCacheObject(CACHE_PREFIX + dynamicPage.getSiteId() + ":" + dynamicPage.getCode(), dynamicPage);
|
||||
}
|
||||
|
||||
public CmsDynamicPage getDynamicPage(Long siteId, String path) {
|
||||
if (path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
return redisCache.getCacheObject(CACHE_PREFIX + siteId + ":" + path);
|
||||
}
|
||||
}
|
||||
@ -24,15 +24,12 @@ import com.chestnut.cms.dynamic.mapper.CmsDynamicPageMapper;
|
||||
import com.chestnut.cms.dynamic.service.IDynamicPageService;
|
||||
import com.chestnut.common.exception.CommonErrorCode;
|
||||
import com.chestnut.common.exception.GlobalException;
|
||||
import com.chestnut.common.redis.RedisCache;
|
||||
import com.chestnut.common.staticize.StaticizeService;
|
||||
import com.chestnut.common.staticize.core.TemplateContext;
|
||||
import com.chestnut.common.utils.Assert;
|
||||
import com.chestnut.common.utils.IdUtils;
|
||||
import com.chestnut.common.utils.ServletUtils;
|
||||
import com.chestnut.common.utils.SpringUtils;
|
||||
import com.chestnut.contentcore.config.CMSConfig;
|
||||
import com.chestnut.contentcore.core.IDynamicPageType;
|
||||
import com.chestnut.contentcore.domain.CmsSite;
|
||||
import com.chestnut.contentcore.service.ISiteService;
|
||||
import com.chestnut.contentcore.service.ITemplateService;
|
||||
@ -67,33 +64,17 @@ public class DynamicPageServiceImpl extends ServiceImpl<CmsDynamicPageMapper, Cm
|
||||
|
||||
private final StaticizeService staticizeService;
|
||||
|
||||
private final RedisCache redisCache;
|
||||
|
||||
private final Map<String, IDynamicPageInitData> dynamicPageInitDataMap;
|
||||
|
||||
private final List<RequestMappingHandlerMapping> allRequestMapping;
|
||||
|
||||
private final DynamicPageRequestMappingHandlerMapping dynamicPageRequestMapping;
|
||||
|
||||
private final Map<String, IDynamicPageType> dynamicPageTypeMap;
|
||||
private final DynamicPageHelper dynamicPageHelper;
|
||||
|
||||
@Override
|
||||
public String getDynamicPagePath(Long siteId, String code) {
|
||||
IDynamicPageType dynamicPageType = this.dynamicPageTypeMap.get(IDynamicPageType.BEAN_PREFIX + code);
|
||||
if (Objects.nonNull(dynamicPageType)) {
|
||||
return dynamicPageType.getRequestPath();
|
||||
}
|
||||
CmsDynamicPage dynamicPage = getDynamicPage(siteId, code);
|
||||
if (Objects.nonNull(dynamicPage)) {
|
||||
return dynamicPage.getPath();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDynamicPage(CmsDynamicPage dynamicPage) {
|
||||
if (!dynamicPage.getPath().startsWith("/")) {
|
||||
dynamicPage.setPath("/" + dynamicPage.getPath());
|
||||
if (dynamicPage.getPath().startsWith("/")) {
|
||||
dynamicPage.setPath(dynamicPage.getPath().substring(1));
|
||||
}
|
||||
this.checkDynamicPage(dynamicPage);
|
||||
|
||||
@ -102,7 +83,7 @@ public class DynamicPageServiceImpl extends ServiceImpl<CmsDynamicPageMapper, Cm
|
||||
|
||||
this.registerDynamicPageMapping(dynamicPage);
|
||||
|
||||
this.updateCache(dynamicPage);
|
||||
dynamicPageHelper.updateCache(dynamicPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -119,7 +100,7 @@ public class DynamicPageServiceImpl extends ServiceImpl<CmsDynamicPageMapper, Cm
|
||||
dbDynamicPage.setTemplates(dynamicPage.getTemplates());
|
||||
this.updateById(dbDynamicPage);
|
||||
|
||||
this.updateCache(dbDynamicPage);
|
||||
dynamicPageHelper.updateCache(dbDynamicPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -129,24 +110,10 @@ public class DynamicPageServiceImpl extends ServiceImpl<CmsDynamicPageMapper, Cm
|
||||
|
||||
dynamicPages.forEach(dynamicPage -> {
|
||||
this.unregisterDynamicPageMapping(dynamicPage);
|
||||
this.clearCache(dynamicPage);
|
||||
dynamicPageHelper.clearCache(dynamicPage);
|
||||
});
|
||||
}
|
||||
|
||||
private void clearCache(CmsDynamicPage dynamicPage) {
|
||||
this.redisCache.deleteObject(CACHE_PREFIX + dynamicPage.getSiteId() + ":" + dynamicPage.getPath());
|
||||
this.redisCache.deleteObject(CACHE_PREFIX + dynamicPage.getSiteId() + ":" + dynamicPage.getCode());
|
||||
}
|
||||
|
||||
private void updateCache(CmsDynamicPage dynamicPage) {
|
||||
this.redisCache.setCacheObject(CACHE_PREFIX + dynamicPage.getSiteId() + ":" + dynamicPage.getPath(), dynamicPage);
|
||||
this.redisCache.setCacheObject(CACHE_PREFIX + dynamicPage.getSiteId() + ":" + dynamicPage.getCode(), dynamicPage);
|
||||
}
|
||||
|
||||
private CmsDynamicPage getDynamicPage(Long siteId, String path) {
|
||||
return redisCache.getCacheObject(CACHE_PREFIX + siteId + ":" + path);
|
||||
}
|
||||
|
||||
private void checkDynamicPage(CmsDynamicPage dynamicPage) {
|
||||
Long count = this.lambdaQuery()
|
||||
.and(wrapper -> wrapper.eq(CmsDynamicPage::getPath, dynamicPage.getPath())
|
||||
@ -201,7 +168,7 @@ public class DynamicPageServiceImpl extends ServiceImpl<CmsDynamicPageMapper, Cm
|
||||
public void run(String... args) throws Exception {
|
||||
// 初始化DynamicPageRequestMapping
|
||||
this.list().forEach(dynamicPage -> {
|
||||
this.updateCache(dynamicPage);
|
||||
dynamicPageHelper.updateCache(dynamicPage);
|
||||
|
||||
this.registerDynamicPageMapping(dynamicPage);
|
||||
});
|
||||
@ -218,7 +185,7 @@ public class DynamicPageServiceImpl extends ServiceImpl<CmsDynamicPageMapper, Cm
|
||||
this.catchException("/", response, new RuntimeException("Site not found: " + siteId));
|
||||
return;
|
||||
}
|
||||
CmsDynamicPage dynamicPage = this.getDynamicPage(siteId, requestURI);
|
||||
CmsDynamicPage dynamicPage = dynamicPageHelper.getDynamicPage(siteId, requestURI);
|
||||
String template = dynamicPage.getTemplates().get(publishPipeCode);
|
||||
File templateFile = this.templateService.findTemplateFile(site, template, publishPipeCode);
|
||||
if (Objects.isNull(templateFile) || !templateFile.exists()) {
|
||||
@ -236,13 +203,13 @@ public class DynamicPageServiceImpl extends ServiceImpl<CmsDynamicPageMapper, Cm
|
||||
// init template datamode
|
||||
TemplateUtils.initGlobalVariables(site, templateContext);
|
||||
// init templateType data to datamode
|
||||
templateContext.getVariables().put("Request", ServletUtils.getParameters());
|
||||
templateContext.getVariables().put("Request", parameters);
|
||||
// 动态页面自定义数据
|
||||
if (Objects.nonNull(dynamicPage.getInitDataTypes())) {
|
||||
dynamicPage.getInitDataTypes().forEach(initDataType -> {
|
||||
IDynamicPageInitData initData = dynamicPageInitDataMap.get(IDynamicPageInitData.BEAN_PREFIX + initDataType);
|
||||
IDynamicPageInitData initData = dynamicPageHelper.getDynamicPageInitData(initDataType);
|
||||
if (Objects.nonNull(initData)) {
|
||||
initData.initTemplateData(templateContext);
|
||||
initData.initTemplateData(templateContext, parameters);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chestnut.cms.dynamic.template.func;
|
||||
|
||||
import com.chestnut.cms.dynamic.service.impl.DynamicPageHelper;
|
||||
import com.chestnut.common.staticize.FreeMarkerUtils;
|
||||
import com.chestnut.common.staticize.core.TemplateContext;
|
||||
import com.chestnut.common.staticize.func.AbstractFunc;
|
||||
import com.chestnut.common.utils.StringUtils;
|
||||
import com.chestnut.contentcore.util.TemplateUtils;
|
||||
import freemarker.core.Environment;
|
||||
import freemarker.template.TemplateBooleanModel;
|
||||
import freemarker.template.TemplateModelException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Freemarker模板自定义函数:获取自定义动态模板链接
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class CustomDynamicPageLinkFunction extends AbstractFunc {
|
||||
|
||||
static final String FUNC_NAME = "customDynamicPageLink";
|
||||
|
||||
private static final String DESC = "{FREEMARKER.FUNC.DESC." + FUNC_NAME + "}";
|
||||
|
||||
private final DynamicPageHelper dynamicPageHelper;
|
||||
|
||||
@Override
|
||||
public String getFuncName() {
|
||||
return FUNC_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return DESC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object exec0(Object... args) throws TemplateModelException {
|
||||
if (args.length < 1) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
String code = args[0].toString(); // 自动动态模板编码
|
||||
|
||||
Environment env = Environment.getCurrentEnvironment();
|
||||
TemplateContext context = FreeMarkerUtils.getTemplateContext(env);
|
||||
long siteId = FreeMarkerUtils.evalLongVariable(env, "Site.siteId");
|
||||
String path = this.dynamicPageHelper.getDynamicPagePath(siteId, code);
|
||||
if (StringUtils.isEmpty(path)) {
|
||||
throw new TemplateModelException("Unknown dynamic page code: " + code);
|
||||
}
|
||||
if (context.isPreview()) {
|
||||
path += "?preview=true";
|
||||
path = TemplateUtils.appendTokenParameter(path, Environment.getCurrentEnvironment());
|
||||
}
|
||||
if (args.length == 2) {
|
||||
String queryString = args[1].toString();
|
||||
path += (path.contains("?") ? "&" : "?") + queryString;
|
||||
}
|
||||
boolean ignoreBaseArg = true;
|
||||
if (args.length == 3) {
|
||||
ignoreBaseArg = ((TemplateBooleanModel) args[2]).getAsBoolean();
|
||||
}
|
||||
if (context.isPreview() || !ignoreBaseArg) {
|
||||
path += (path.contains("?") ? "&" : "?") + "sid=" + siteId + "&pp=" + context.getPublishPipeCode();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FuncArg> getFuncArgs() {
|
||||
return List.of(
|
||||
new FuncArg("动态页面编码", FuncArgType.String, true, null),
|
||||
new FuncArg("自定义参数", FuncArgType.String, false, null),
|
||||
new FuncArg("忽略sid/pp参数", FuncArgType.String, false, "默认:true")
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
DYNAMIC_PAGE_INIT_DATA.Member=登录会员
|
||||
DYNAMIC_PAGE_INIT_DATA.Pagination=分页参数
|
||||
|
||||
FREEMARKER.FUNC.DESC.customDynamicPageLink=获取自定义动态模板页面链接,例如:${customDynamicPageLink('Test','a=1&b=2',true)}
|
||||
@ -0,0 +1,4 @@
|
||||
DYNAMIC_PAGE_INIT_DATA.Member=Member
|
||||
DYNAMIC_PAGE_INIT_DATA.Pagination=Pagination
|
||||
|
||||
FREEMARKER.FUNC.DESC.customDynamicPageLink=Get custom dynamic page link, eg: ${customDynamicPageLink('Test','a=1&b=2',true)}
|
||||
@ -0,0 +1,4 @@
|
||||
DYNAMIC_PAGE_INIT_DATA.Member=登錄會員
|
||||
DYNAMIC_PAGE_INIT_DATA.Pagination=分頁參數
|
||||
|
||||
FREEMARKER.FUNC.DESC.customDynamicPageLink=獲取自定義動態模板頁面鏈接,例如:${customDynamicPageLink('Test','a=1&b=2',true)}
|
||||
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.chestnut</groupId>
|
||||
<artifactId>chestnut-cms</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chestnut-cms-exmodel</artifactId>
|
||||
|
||||
@ -59,6 +59,8 @@ import java.util.regex.Matcher;
|
||||
@RequiredArgsConstructor
|
||||
public class EXModelCoreDataHandler implements ICoreDataHandler {
|
||||
|
||||
private static final String XMODEL_TABLE_SUFFIX = "_cms_exmodel";
|
||||
|
||||
private final ExtendModelMapper modelDataMapper;
|
||||
|
||||
private final IModelService modelService;
|
||||
@ -88,11 +90,11 @@ public class EXModelCoreDataHandler implements ICoreDataHandler {
|
||||
|
||||
// 扩展模型
|
||||
List<XModel> modelList = this.modelService.lambdaQuery().in(XModel::getModelId, modelIdStrings).list();
|
||||
context.saveData(XModel.TABLE_NAME, JacksonUtils.to(modelList));
|
||||
context.saveData(XModel.TABLE_NAME + XMODEL_TABLE_SUFFIX, JacksonUtils.to(modelList));
|
||||
// 扩展模型字段
|
||||
List<XModelField> fieldList = this.modelFieldService.lambdaQuery()
|
||||
.in(XModelField::getModelId, modelIdStrings).list();
|
||||
context.saveData(XModelField.TABLE_NAME, JacksonUtils.to(fieldList));
|
||||
context.saveData(XModelField.TABLE_NAME + XMODEL_TABLE_SUFFIX, JacksonUtils.to(fieldList));
|
||||
// 扩展模型数据
|
||||
int pageSize = 200;
|
||||
int fileIndex = 1;
|
||||
@ -117,7 +119,7 @@ public class EXModelCoreDataHandler implements ICoreDataHandler {
|
||||
AsyncTaskManager.setTaskTenPercentProgressInfo("正在导入扩展模型");
|
||||
// XModel
|
||||
Map<Long, Long> modelIdMapping = new HashMap<>();
|
||||
List<File> files = context.readDataFiles(XModel.TABLE_NAME);
|
||||
List<File> files = context.readDataFiles(XModel.TABLE_NAME + XMODEL_TABLE_SUFFIX);
|
||||
files.forEach(f -> {
|
||||
List<XModel> list = JacksonUtils.fromList(f, XModel.class);
|
||||
for (XModel data : list) {
|
||||
@ -141,7 +143,7 @@ public class EXModelCoreDataHandler implements ICoreDataHandler {
|
||||
});
|
||||
// XModelField
|
||||
AsyncTaskManager.setTaskTenPercentProgressInfo("正在导入扩展模型字段");
|
||||
files = context.readDataFiles(XModelField.TABLE_NAME);
|
||||
files = context.readDataFiles(XModelField.TABLE_NAME + XMODEL_TABLE_SUFFIX);
|
||||
files.forEach(f -> {
|
||||
List<XModelField> list = JacksonUtils.fromList(f, XModelField.class);
|
||||
for (XModelField data : list) {
|
||||
@ -198,7 +200,7 @@ public class EXModelCoreDataHandler implements ICoreDataHandler {
|
||||
});
|
||||
modelDataMapper.insert(data);
|
||||
} catch (Exception e) {
|
||||
AsyncTaskManager.addErrMessage("导入扩展模型字段失败:" + data.getModelId()
|
||||
AsyncTaskManager.addErrMessage("导入扩展模型数据失败:" + data.getModelId()
|
||||
+ data.getDataType() + "-" + data.getDataId());
|
||||
log.error("Import xmodel data failed: {} - {}", data.getModelId(), data.getDataId(), e);
|
||||
}
|
||||
|
||||
@ -52,9 +52,9 @@ public class MetaControlType_CmsImage implements IMetaControlType {
|
||||
String imagePath = value.toString();
|
||||
if (InternalUrlUtils.isInternalUrl(imagePath)) {
|
||||
String previewUrl = InternalUrlUtils.getActualPreviewUrl(imagePath);
|
||||
fieldData.setValueSrc(previewUrl);
|
||||
fieldData.setValueObj(previewUrl);
|
||||
} else {
|
||||
fieldData.setValueSrc(imagePath);
|
||||
fieldData.setValueObj(imagePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chestnut.exmodel;
|
||||
|
||||
import com.chestnut.common.utils.StringUtils;
|
||||
import com.chestnut.contentcore.domain.CmsContent;
|
||||
import com.chestnut.contentcore.service.IContentService;
|
||||
import com.chestnut.xmodel.core.IMetaControlType;
|
||||
import com.chestnut.xmodel.dto.XModelFieldDataDTO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 元数据模型字段控件类型:内容选择
|
||||
*
|
||||
* @author 兮玥
|
||||
* @email 190785909@qq.com
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Component(IMetaControlType.BEAN_PREFIX + MetaControlType_ContentSelect.TYPE)
|
||||
public class MetaControlType_ContentSelect implements IMetaControlType {
|
||||
|
||||
public static final String TYPE = "CMSContentSelect";
|
||||
|
||||
private final IContentService contentService;
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "{META.CONTROL_TYPE." + TYPE + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseFieldValue(XModelFieldDataDTO fieldData) {
|
||||
Object value = fieldData.getValue();
|
||||
if (Objects.isNull(value) || StringUtils.isEmpty(value.toString())) {
|
||||
fieldData.setValueObj(Map.of());
|
||||
return;
|
||||
}
|
||||
Optional<CmsContent> content = contentService.lambdaQuery()
|
||||
.select(List.of(CmsContent::getContentId, CmsContent::getTitle))
|
||||
.eq(CmsContent::getContentId, value).oneOpt();
|
||||
if (content.isEmpty()) {
|
||||
fieldData.setValue(StringUtils.EMPTY);
|
||||
fieldData.setValueObj(Map.of());
|
||||
return;
|
||||
}
|
||||
fieldData.setValueObj(Map.of(
|
||||
"contentId", content.get().getContentId(),
|
||||
"title", content.get().getTitle()
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -12,3 +12,4 @@ DICT.ExtendModelDataType.content=内容
|
||||
|
||||
META.CONTROL_TYPE.CMSImage=图片上传
|
||||
META.CONTROL_TYPE.UEditor=富文本编辑器
|
||||
META.CONTROL_TYPE.CMSContentSelect=内容选择
|
||||
@ -12,3 +12,4 @@ DICT.ExtendModelDataType.content=Content
|
||||
|
||||
META.CONTROL_TYPE.CMSImage=Image Upload
|
||||
META.CONTROL_TYPE.UEditor=Rich Text Editor
|
||||
META.CONTROL_TYPE.CMSContentSelect=Content Selector
|
||||
@ -12,3 +12,4 @@ DICT.ExtendModelDataType.content=內容
|
||||
|
||||
META.CONTROL_TYPE.CMSImage=圖片上傳
|
||||
META.CONTROL_TYPE.UEditor=富文本編輯器
|
||||
META.CONTROL_TYPE.CMSContentSelect=內容選擇
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.chestnut</groupId>
|
||||
<artifactId>chestnut-cms</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chestnut-cms-image</artifactId>
|
||||
|
||||
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chestnut.cms.image;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.chestnut.cms.image.domain.CmsImage;
|
||||
import com.chestnut.cms.image.service.IImageService;
|
||||
import com.chestnut.common.async.AsyncTaskManager;
|
||||
import com.chestnut.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 lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 图集内容扩展内容核心数据处理器
|
||||
*
|
||||
* @author 兮玥
|
||||
* @email 190785909@qq.com
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ImageCoreDataHandler implements ICoreDataHandler {
|
||||
|
||||
private final IImageService imageService;
|
||||
|
||||
@Override
|
||||
public void onSiteExport(SiteExportContext context) {
|
||||
// cms_image
|
||||
AsyncTaskManager.setTaskTenPercentProgressInfo("正在导出图集内容数据");
|
||||
int pageSize = 200;
|
||||
long offset = 0;
|
||||
int fileIndex = 1;
|
||||
while (true) {
|
||||
LambdaQueryWrapper<CmsImage> q = new LambdaQueryWrapper<CmsImage>()
|
||||
.eq(CmsImage::getSiteId, context.getSite().getSiteId())
|
||||
.gt(CmsImage::getImageId, offset)
|
||||
.orderByAsc(CmsImage::getImageId);
|
||||
Page<CmsImage> page = imageService.page(new Page<>(1, pageSize, false), q);
|
||||
if (!page.getRecords().isEmpty()) {
|
||||
context.saveData(CmsImage.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_image
|
||||
AsyncTaskManager.setTaskTenPercentProgressInfo("正在导入图集内容数据");
|
||||
List<File> files = context.readDataFiles(CmsImage.TABLE_NAME);
|
||||
files.forEach(f -> {
|
||||
List<CmsImage> list = JacksonUtils.fromList(f, CmsImage.class);
|
||||
for (CmsImage data : list) {
|
||||
try {
|
||||
data.setImageId(IdUtils.getSnowflakeId());
|
||||
data.setSiteId(context.getSite().getSiteId());
|
||||
data.setContentId(context.getContentIdMap().get(data.getContentId()));
|
||||
data.createBy(context.getOperator());
|
||||
data.setPath(context.dealInternalUrl(data.getPath()));
|
||||
imageService.save(data);
|
||||
} catch (Exception e) {
|
||||
AsyncTaskManager.addErrMessage("导入图集内容数据失败:" + data.getTitle()
|
||||
+ "[" + data.getImageId() + "]");
|
||||
log.error("Import cms_image failed: {}", data.getImageId(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.chestnut</groupId>
|
||||
<artifactId>chestnut-cms</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chestnut-cms-link</artifactId>
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.chestnut</groupId>
|
||||
<artifactId>chestnut-cms</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chestnut-cms-media</artifactId>
|
||||
|
||||
@ -77,7 +77,7 @@ public class CmsVideoTag extends AbstractListTag {
|
||||
q.orderByAsc(CmsVideo::getSortFlag);
|
||||
|
||||
Page<CmsVideo> pageResult = this.videoService.page(new Page<>(pageIndex, size, page), q);
|
||||
if (pageIndex > 1 & pageResult.getRecords().size() == 0) {
|
||||
if (pageIndex > 1 & pageResult.getRecords().isEmpty()) {
|
||||
throw new TemplateException("内容列表页码超出上限:" + pageIndex, env);
|
||||
}
|
||||
TemplateContext context = FreeMarkerUtils.getTemplateContext(env);
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.chestnut</groupId>
|
||||
<artifactId>chestnut-cms</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chestnut-cms-member</artifactId>
|
||||
|
||||
@ -63,7 +63,7 @@ public class AccountBindEmailDynamicPageType implements IDynamicPageType {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "会员绑定邮箱页";
|
||||
return "{DYNAMIC_PAGE_TYPE.NAME." + TYPE + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -59,7 +59,7 @@ public class AccountCentreDynamicPageType implements IDynamicPageType {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "个人中心页";
|
||||
return "{DYNAMIC_PAGE_TYPE.NAME." + TYPE + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -76,7 +76,7 @@ public class AccountContributeDynamicPageType implements IDynamicPageType {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "会员投稿页";
|
||||
return "{DYNAMIC_PAGE_TYPE.NAME." + TYPE + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -47,7 +47,7 @@ public class AccountForgetPasswordDynamicPageType implements IDynamicPageType {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "会员找回密码页";
|
||||
return "{DYNAMIC_PAGE_TYPE.NAME." + TYPE + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -47,7 +47,7 @@ public class AccountLoginDynamicPageType implements IDynamicPageType {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "会员登录页";
|
||||
return "{DYNAMIC_PAGE_TYPE.NAME." + TYPE + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -58,12 +58,12 @@ public class AccountPasswordDynamicPageType implements IDynamicPageType {
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return TYPE;
|
||||
return "{DYNAMIC_PAGE_TYPE.NAME." + TYPE + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "会员修改密码页";
|
||||
return "{DYNAMIC_PAGE_TYPE.NAME." + TYPE + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -47,7 +47,7 @@ public class AccountRegisterDynamicPageType implements IDynamicPageType {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "会员注册页";
|
||||
return "{DYNAMIC_PAGE_TYPE.NAME." + TYPE + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -63,7 +63,7 @@ public class AccountSettingDynamicPageType implements IDynamicPageType {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "会员设置页";
|
||||
return "{DYNAMIC_PAGE_TYPE.NAME." + TYPE + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -9,3 +9,13 @@ FREEMARKER.TAG.DESC.cms_member_follow=获取关注/粉丝数据列表,内嵌<#
|
||||
|
||||
# freemarker模板函数
|
||||
FREEMARKER.FUNC.DESC.accountUrl=会员中心动态页面地址,例如:${accountUrl('comment')}
|
||||
|
||||
# 动态模板
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountBindEmail=会员绑定邮箱页
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountCentre=会员个人中心页
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountContribute=会员投稿页
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountForgetPassword=会员找回密码页
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountLogin=会员登录页
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountPassword=会员修改密码页
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountRegister=会员注册页
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountSetting=会员设置页
|
||||
@ -9,3 +9,13 @@ FREEMARKER.TAG.DESC.cms_member_follow=Fetch member follow/follower list, use <#l
|
||||
|
||||
# freemarker模板函数
|
||||
FREEMARKER.FUNC.DESC.accountUrl=Account centre page url, eg: ${accountUrl('comment')}.
|
||||
|
||||
# 动态模板
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountBindEmail=Account binding email page
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountCentre=Account centre page
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountContribute=Account contribute page
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountForgetPassword=Account forget password page
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountLogin=Account login page
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountPassword=Account modify password page
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountRegister=Account register page
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountSetting=Account setting page
|
||||
@ -9,3 +9,13 @@ FREEMARKER.TAG.DESC.cms_member_follow=獲取關注/粉絲數據列表,內嵌<#
|
||||
|
||||
# freemarker模板函數
|
||||
FREEMARKER.FUNC.DESC.accountUrl=會員中心動態頁面地址,例如:${accountUrl('comment')}
|
||||
|
||||
# 动态模板
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountBindEmail=會員綁定郵箱頁
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountCentre=會員個人中心頁
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountContribute=會員投稿頁
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountForgetPassword=會員找回密碼頁
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountLogin=會員登錄頁
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountPassword=會員修改密碼頁
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountRegister=會員註冊頁
|
||||
DYNAMIC_PAGE_TYPE.NAME.AccountSetting=會員設置頁
|
||||
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.chestnut</groupId>
|
||||
<artifactId>chestnut-cms</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chestnut-cms-search</artifactId>
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chestnut.cms.search;
|
||||
|
||||
/**
|
||||
* CmsSearchConstants
|
||||
*
|
||||
* @author 兮玥
|
||||
* @email 190785909@qq.com
|
||||
*/
|
||||
public interface CmsSearchConstants {
|
||||
|
||||
String SEARCH_SOURCE_PREFIX = "cms:";
|
||||
|
||||
static String generateSearchSource(Long siteId) {
|
||||
return SEARCH_SOURCE_PREFIX + siteId;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chestnut.cms.search.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.chestnut.cms.search.CmsSearchConstants;
|
||||
import com.chestnut.common.domain.R;
|
||||
import com.chestnut.common.security.anno.Priv;
|
||||
import com.chestnut.common.security.web.BaseRestController;
|
||||
import com.chestnut.common.security.web.PageRequest;
|
||||
import com.chestnut.common.utils.ServletUtils;
|
||||
import com.chestnut.common.utils.StringUtils;
|
||||
import com.chestnut.contentcore.domain.CmsSite;
|
||||
import com.chestnut.contentcore.service.ISiteService;
|
||||
import com.chestnut.search.domain.SearchLog;
|
||||
import com.chestnut.search.domain.SearchWord;
|
||||
import com.chestnut.search.service.ISearchLogService;
|
||||
import com.chestnut.system.security.AdminUserType;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/cms/search/log")
|
||||
public class CMSSearchLogController extends BaseRestController {
|
||||
|
||||
private final ISiteService siteService;
|
||||
|
||||
private final ISearchLogService searchLogService;
|
||||
|
||||
@Priv(type = AdminUserType.TYPE)
|
||||
@GetMapping
|
||||
public R<?> getPageList(@RequestParam(value = "query", required = false) String query) {
|
||||
PageRequest pr = this.getPageRequest();
|
||||
CmsSite site = this.siteService.getCurrentSite(ServletUtils.getRequest());
|
||||
Page<SearchLog> page = this.searchLogService.lambdaQuery()
|
||||
.eq(SearchLog::getSource, CmsSearchConstants.generateSearchSource(site.getSiteId()))
|
||||
.like(StringUtils.isNotEmpty(query), SearchLog::getWord, query)
|
||||
.orderByDesc(SearchLog::getLogId)
|
||||
.page(new Page<>(pr.getPageNumber(), pr.getPageSize(), true));
|
||||
return this.bindDataTable(page);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chestnut.cms.search.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.chestnut.cms.search.CmsSearchConstants;
|
||||
import com.chestnut.common.domain.R;
|
||||
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.web.BaseRestController;
|
||||
import com.chestnut.common.security.web.PageRequest;
|
||||
import com.chestnut.common.utils.ServletUtils;
|
||||
import com.chestnut.common.utils.StringUtils;
|
||||
import com.chestnut.contentcore.domain.CmsSite;
|
||||
import com.chestnut.contentcore.service.ISiteService;
|
||||
import com.chestnut.search.domain.SearchWord;
|
||||
import com.chestnut.search.service.ISearchWordService;
|
||||
import com.chestnut.system.security.AdminUserType;
|
||||
import com.chestnut.system.security.StpAdminUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Priv(type = AdminUserType.TYPE)
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/cms/search/word")
|
||||
public class CMSSearchWordStatController extends BaseRestController {
|
||||
|
||||
private final ISiteService siteService;
|
||||
|
||||
private final ISearchWordService searchWordStatService;
|
||||
|
||||
@GetMapping
|
||||
public R<?> getPageList(@RequestParam(value = "query", required = false) String query) {
|
||||
PageRequest pr = this.getPageRequest();
|
||||
CmsSite site = this.siteService.getCurrentSite(ServletUtils.getRequest());
|
||||
Page<SearchWord> page = this.searchWordStatService.lambdaQuery()
|
||||
.eq(SearchWord::getSource, CmsSearchConstants.generateSearchSource(site.getSiteId()))
|
||||
.like(StringUtils.isNotEmpty(query), SearchWord::getWord, query)
|
||||
.orderByDesc(SearchWord::getTopFlag, SearchWord::getSearchTotal)
|
||||
.page(new Page<>(pr.getPageNumber(), pr.getPageSize(), true));
|
||||
return this.bindDataTable(page);
|
||||
}
|
||||
|
||||
@Log(title = "新增搜索词", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public R<?> addWord(@RequestBody SearchWord wordStat) {
|
||||
CmsSite site = this.siteService.getCurrentSite(ServletUtils.getRequest());
|
||||
wordStat.setSource(CmsSearchConstants.generateSearchSource(site.getSiteId()));
|
||||
wordStat.createBy(StpAdminUtil.getLoginUser().getUsername());
|
||||
this.searchWordStatService.addWord(wordStat);
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
@ -19,6 +19,7 @@ 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.chestnut.cms.search.CmsSearchConstants;
|
||||
import com.chestnut.cms.search.es.doc.ESContent;
|
||||
import com.chestnut.cms.search.vo.ESContentVO;
|
||||
import com.chestnut.common.domain.R;
|
||||
@ -153,7 +154,7 @@ public class SearchApiController extends BaseRestController {
|
||||
c.setCommentCount(cdd.getComments());
|
||||
});
|
||||
// 记录搜索日志
|
||||
this.logService.addSearchLog("site:" + siteId, query, ServletUtils.getRequest());
|
||||
this.logService.addSearchLog(CmsSearchConstants.generateSearchSource(siteId), query, ServletUtils.getRequest());
|
||||
return this.bindDataTable(list, Objects.isNull(sr.hits().total()) ? 0 : sr.hits().total().value());
|
||||
}
|
||||
|
||||
|
||||
@ -58,7 +58,7 @@ public class SearchDynamicPageType implements IDynamicPageType {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "搜索结果页";
|
||||
return "{DYNAMIC_PAGE_TYPE.NAME." + TYPE + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.chestnut.cms.search.properties;
|
||||
|
||||
import com.chestnut.common.utils.StringUtils;
|
||||
import com.chestnut.contentcore.core.IProperty;
|
||||
import com.chestnut.contentcore.util.ConfigPropertyUtils;
|
||||
import com.chestnut.system.fixed.dict.YesOrNo;
|
||||
@ -30,6 +31,8 @@ public class EnableIndexProperty implements IProperty {
|
||||
|
||||
public final static String ID = "EnableIndex";
|
||||
|
||||
private final static String DEFAULT_VALUE = YesOrNo.YES;
|
||||
|
||||
static UseType[] UseTypes = new UseType[] { UseType.Site, UseType.Catalog };
|
||||
|
||||
@Override
|
||||
@ -49,11 +52,14 @@ public class EnableIndexProperty implements IProperty {
|
||||
|
||||
@Override
|
||||
public String defaultValue() {
|
||||
return YesOrNo.YES;
|
||||
return DEFAULT_VALUE;
|
||||
}
|
||||
|
||||
public static String getValue(Map<String, String> firstConfigProps, Map<String, String> secondConfigProps) {
|
||||
String value = ConfigPropertyUtils.getStringValue(ID, firstConfigProps, secondConfigProps);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
value = DEFAULT_VALUE;
|
||||
}
|
||||
return YesOrNo.isYes(value) ? value : YesOrNo.NO;
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,7 +29,6 @@ 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;
|
||||
@ -105,7 +104,7 @@ public class ContentIndexService implements CommandLineRunner {
|
||||
Assert.isTrue(response.acknowledged(), () -> new RuntimeException("Create Index[cms_content] failed."));
|
||||
}
|
||||
|
||||
public void recreateIndex(CmsSite site) throws IOException {
|
||||
public void deleteContentIndices(CmsSite site) throws IOException {
|
||||
boolean exists = esClient.indices().exists(fn -> fn.index(ESContent.INDEX_NAME)).value();
|
||||
if (exists) {
|
||||
// 删除站点索引文档数据
|
||||
@ -119,9 +118,7 @@ public class ContentIndexService implements CommandLineRunner {
|
||||
.getRecords().stream().map(CmsContent::getContentId).toList();
|
||||
deleteContentDoc(contentIds);
|
||||
}
|
||||
esClient.indices().delete(fn -> fn.index(ESContent.INDEX_NAME));
|
||||
}
|
||||
this.createIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -219,8 +216,8 @@ public class ContentIndexService implements CommandLineRunner {
|
||||
|
||||
@Override
|
||||
public void run0() throws Exception {
|
||||
// 先重建索引
|
||||
recreateIndex(site);
|
||||
// 先删除内容索引 TODO batchDelete
|
||||
deleteContentIndices(site);
|
||||
|
||||
List<CmsCatalog> catalogs = catalogService.lambdaQuery()
|
||||
.eq(CmsCatalog::getSiteId, site.getSiteId()).list();
|
||||
|
||||
@ -47,6 +47,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
@Deprecated(since = "1.4.2", forRemoval = true)
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class CmsRelaContentTag extends AbstractListTag {
|
||||
|
||||
@ -17,6 +17,7 @@ package com.chestnut.cms.search.template.tag;
|
||||
|
||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||
import co.elastic.clients.elasticsearch._types.SortOrder;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
|
||||
import co.elastic.clients.elasticsearch.core.SearchResponse;
|
||||
import com.chestnut.cms.search.es.doc.ESContent;
|
||||
import com.chestnut.cms.search.vo.ESContentVO;
|
||||
@ -36,6 +37,7 @@ import freemarker.template.TemplateException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
import org.ehcache.shadow.org.terracotta.context.query.QueryBuilder;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -64,7 +66,7 @@ public class CmsSearchContentTag extends AbstractListTag {
|
||||
@Override
|
||||
public List<TagAttr> getTagAttrs() {
|
||||
List<TagAttr> tagAttrs = super.getTagAttrs();
|
||||
tagAttrs.add(new TagAttr(ATTR_QUERY, true, TagAttrDataType.STRING, "检索词"));
|
||||
tagAttrs.add(new TagAttr(ATTR_QUERY, false, 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, "检索方式",
|
||||
@ -77,9 +79,9 @@ public class CmsSearchContentTag extends AbstractListTag {
|
||||
long siteId = FreeMarkerUtils.evalLongVariable(env, "Site.siteId");
|
||||
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);
|
||||
}
|
||||
// if (StringUtils.isEmpty(query)) {
|
||||
// throw new TemplateException("Tag attr `query` cannot be empty.", env);
|
||||
// }
|
||||
String contentType = MapUtils.getString(attrs, ATTR_CONTENT_TYPE);
|
||||
Long catalogId = MapUtils.getLong(attrs, ATTR_CATALOG_ID);
|
||||
try {
|
||||
@ -94,6 +96,7 @@ public class CmsSearchContentTag extends AbstractListTag {
|
||||
if (IdUtils.validate(catalogId)) {
|
||||
b.must(must -> must.term(tq -> tq.field("catalogId").value(catalogId)));
|
||||
}
|
||||
if (StringUtils.isNotEmpty(query)) {
|
||||
if (SearchMode.isFullText(mode)) {
|
||||
b.must(must -> must
|
||||
.multiMatch(match -> match
|
||||
@ -102,18 +105,30 @@ public class CmsSearchContentTag extends AbstractListTag {
|
||||
.query(query)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
b.must(should -> {
|
||||
} else if (SearchMode.isTagAnd(mode)) {
|
||||
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))));
|
||||
if (StringUtils.isNotEmpty(keyword)) {
|
||||
b.must(must -> must.match(term -> term.field("tags").query(keyword)));
|
||||
}
|
||||
return should;
|
||||
}
|
||||
} else {
|
||||
b.must(must -> {
|
||||
String[] keywords = StringUtils.split(query, ",");
|
||||
for (String keyword : keywords ) {
|
||||
if (StringUtils.isNotEmpty(keyword)) {
|
||||
must.match(m ->
|
||||
m.field("tags").query(keyword));
|
||||
// must.constantScore(cs ->
|
||||
// cs.boost(1F).filter(f ->
|
||||
// f.match(m ->
|
||||
// m.field("tags").query(keyword))));
|
||||
}
|
||||
}
|
||||
return must;
|
||||
});
|
||||
}
|
||||
}
|
||||
return b;
|
||||
})
|
||||
);
|
||||
@ -184,7 +199,9 @@ public class CmsSearchContentTag extends AbstractListTag {
|
||||
// 所有站点
|
||||
FullText("全文检索"),
|
||||
// 当前站点
|
||||
Tag("标签检索,多个标签英文逗号隔开");
|
||||
Tag("标签检索,多个标签英文逗号隔开"),
|
||||
// 当前站点
|
||||
TagAnd("标签检索,多个标签英文逗号隔开");
|
||||
|
||||
private final String desc;
|
||||
|
||||
@ -200,10 +217,15 @@ public class CmsSearchContentTag extends AbstractListTag {
|
||||
return Tag.name().equalsIgnoreCase(mode);
|
||||
}
|
||||
|
||||
static boolean isTagAnd(String mode) {
|
||||
return TagAnd.name().equalsIgnoreCase(mode);
|
||||
}
|
||||
|
||||
static List<TagAttrOption> toTagAttrOptions() {
|
||||
return List.of(
|
||||
new TagAttrOption(FullText.name(), FullText.desc),
|
||||
new TagAttrOption(Tag.name(), Tag.desc)
|
||||
new TagAttrOption(Tag.name(), Tag.desc),
|
||||
new TagAttrOption(TagAnd.name(), Tag.desc)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chestnut.cms.search.template.tag;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.chestnut.cms.search.CmsSearchConstants;
|
||||
import com.chestnut.common.staticize.FreeMarkerUtils;
|
||||
import com.chestnut.common.staticize.tag.AbstractListTag;
|
||||
import com.chestnut.search.domain.SearchWord;
|
||||
import com.chestnut.search.service.ISearchWordService;
|
||||
import freemarker.core.Environment;
|
||||
import freemarker.template.TemplateException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class CmsSearchWordTag extends AbstractListTag {
|
||||
|
||||
public final static String TAG_NAME = "cms_search_word";
|
||||
|
||||
public final static String NAME = "{FREEMARKER.TAG.NAME." + TAG_NAME + "}";
|
||||
public final static String DESC = "{FREEMARKER.TAG.DESC." + TAG_NAME + "}";
|
||||
|
||||
private final ISearchWordService searchWordService;
|
||||
|
||||
@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 source = CmsSearchConstants.generateSearchSource(siteId);
|
||||
Page<SearchWord> pageResult = this.searchWordService.lambdaQuery()
|
||||
.eq(SearchWord::getSource, source)
|
||||
.orderByDesc(SearchWord::getTopFlag, SearchWord::getSearchTotal)
|
||||
.page(new Page<>(pageIndex, size, page));
|
||||
return TagPageData.of(pageResult.getRecords(), pageResult.getTotal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTagName() {
|
||||
return TAG_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return DESC;
|
||||
}
|
||||
}
|
||||
@ -2,5 +2,9 @@ FREEMARKER.TAG.NAME.cms_rela_content=相关内容标签
|
||||
FREEMARKER.TAG.DESC.cms_rela_content=相关内容标签
|
||||
FREEMARKER.TAG.NAME.cms_search_content=检索内容标签
|
||||
FREEMARKER.TAG.DESC.cms_search_content=检索内容标签
|
||||
FREEMARKER.TAG.NAME.cms_search_word=搜索热词标签
|
||||
FREEMARKER.TAG.DESC.cms_search_word=搜索热词标签
|
||||
|
||||
CONFIG.CMSSearchAnalyzeType=索引分词方式
|
||||
|
||||
DYNAMIC_PAGE_TYPE.NAME.Search=搜索结果页
|
||||
@ -2,5 +2,9 @@ FREEMARKER.TAG.NAME.cms_rela_content=Rela content tag
|
||||
FREEMARKER.TAG.DESC.cms_rela_content=Rela content tag
|
||||
FREEMARKER.TAG.NAME.cms_search_content=Search content tag
|
||||
FREEMARKER.TAG.DESC.cms_search_content=Search content tag
|
||||
FREEMARKER.TAG.NAME.cms_search_word=Search hot word tag
|
||||
FREEMARKER.TAG.DESC.cms_search_word=Search hot word tag
|
||||
|
||||
CONFIG.CMSSearchAnalyzeType=ES Index Analyze Type
|
||||
|
||||
DYNAMIC_PAGE_TYPE.NAME.Search=Search result page
|
||||
@ -2,5 +2,9 @@ FREEMARKER.TAG.NAME.cms_rela_content=相關內容標籤
|
||||
FREEMARKER.TAG.DESC.cms_rela_content=相關內容標籤
|
||||
FREEMARKER.TAG.NAME.cms_search_content=檢索內容標籤
|
||||
FREEMARKER.TAG.DESC.cms_search_content=檢索內容標籤
|
||||
FREEMARKER.TAG.NAME.cms_search_word=檢索詞熱詞標籤
|
||||
FREEMARKER.TAG.DESC.cms_search_word=檢索詞熱詞標籤
|
||||
|
||||
CONFIG.CMSSearchAnalyzeType=索引分詞方式
|
||||
|
||||
DYNAMIC_PAGE_TYPE.NAME.Search=搜索結果頁
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.chestnut</groupId>
|
||||
<artifactId>chestnut-cms</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chestnut-cms-seo</artifactId>
|
||||
|
||||
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chestnut.seo.controller;
|
||||
|
||||
import com.chestnut.common.domain.R;
|
||||
import com.chestnut.common.security.anno.Priv;
|
||||
import com.chestnut.common.security.web.BaseRestController;
|
||||
import com.chestnut.common.utils.ServletUtils;
|
||||
import com.chestnut.contentcore.domain.CmsSite;
|
||||
import com.chestnut.contentcore.service.ISiteService;
|
||||
import com.chestnut.seo.service.BaiduPushService;
|
||||
import com.chestnut.system.security.AdminUserType;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 百度收录推送前端控制器
|
||||
* </p>
|
||||
*
|
||||
* @author 兮玥
|
||||
* @email 190785909@qq.com
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/cms/seo")
|
||||
@RequiredArgsConstructor
|
||||
public class SitePushController extends BaseRestController {
|
||||
|
||||
private final ISiteService siteService;
|
||||
|
||||
private final BaiduPushService baiduPushService;
|
||||
|
||||
@Priv(type = AdminUserType.TYPE)
|
||||
@PostMapping("/baidu_push")
|
||||
public R<?> generateSitemap(@RequestBody @NotEmpty List<Long> contentIds) {
|
||||
CmsSite site = siteService.getCurrentSite(ServletUtils.getRequest());
|
||||
|
||||
List<BaiduPushService.BaiduPushResult> results = baiduPushService.pushContents(site, contentIds);
|
||||
|
||||
return R.ok(results);
|
||||
}
|
||||
}
|
||||
@ -44,7 +44,7 @@ public class SitemapPageType extends FixedDictType {
|
||||
super(TYPE, "{DICT." + TYPE + "}");
|
||||
super.addDictData("{DICT." + TYPE + "." + PC + "}", PC, 1);
|
||||
super.addDictData("{DICT." + TYPE + "." + Mobile + "}", Mobile, 2);
|
||||
super.addDictData("{DICT." + TYPE + ".pc_mobile}", PC_Mobile, 3);
|
||||
super.addDictData("{DICT." + TYPE + "." + PC_Mobile + "}", PC_Mobile, 3);
|
||||
}
|
||||
|
||||
public static <T> void decode(List<T> list, Function<T, String> getter, BiConsumer<T, String> setter) {
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chestnut.seo.properties;
|
||||
|
||||
import com.chestnut.common.utils.StringUtils;
|
||||
import com.chestnut.contentcore.core.IProperty;
|
||||
import com.chestnut.contentcore.util.ConfigPropertyUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 百度收录API秘钥
|
||||
*
|
||||
* @author 兮玥
|
||||
* @email 190785909@qq.com
|
||||
*/
|
||||
@Component(IProperty.BEAN_NAME_PREFIX + BaiduPushAccessSecretProperty.ID)
|
||||
public class BaiduPushAccessSecretProperty implements IProperty {
|
||||
|
||||
public final static String ID = "BaiduPushAccessSecret";
|
||||
|
||||
private final static String DEFAULT_VALUE = StringUtils.EMPTY;
|
||||
|
||||
static UseType[] UseTypes = new UseType[] { UseType.Site };
|
||||
|
||||
@Override
|
||||
public UseType[] getUseTypes() {
|
||||
return UseTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "百度收录API秘钥";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String defaultValue() {
|
||||
return DEFAULT_VALUE;
|
||||
}
|
||||
|
||||
public static String getValue(Map<String, String> configProps) {
|
||||
String value = ConfigPropertyUtils.getStringValue(ID, configProps);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
value = DEFAULT_VALUE;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chestnut.seo.service;
|
||||
|
||||
import com.chestnut.common.utils.HttpUtils;
|
||||
import com.chestnut.common.utils.JacksonUtils;
|
||||
import com.chestnut.common.utils.StringUtils;
|
||||
import com.chestnut.contentcore.domain.CmsContent;
|
||||
import com.chestnut.contentcore.domain.CmsPublishPipe;
|
||||
import com.chestnut.contentcore.domain.CmsSite;
|
||||
import com.chestnut.contentcore.service.IContentService;
|
||||
import com.chestnut.contentcore.service.IPublishPipeService;
|
||||
import com.chestnut.seo.properties.BaiduPushAccessSecretProperty;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* BaiduUrlPusher
|
||||
*
|
||||
* @author 兮玥
|
||||
* @email 190785909@qq.com
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class BaiduPushService {
|
||||
|
||||
private final IContentService contentService;
|
||||
|
||||
private final IPublishPipeService publishPipeService;
|
||||
|
||||
private static final String API = "http://data.zz.baidu.com/urls?site={0}&token={1}";
|
||||
|
||||
public List<BaiduPushResult> pushContents(CmsSite site, List<Long> contentIds) {
|
||||
String secret = BaiduPushAccessSecretProperty.getValue(site.getConfigProps());
|
||||
if (StringUtils.isEmpty(secret)) {
|
||||
return List.of();
|
||||
}
|
||||
List<CmsPublishPipe> publishPipes = publishPipeService.getPublishPipes(site.getSiteId());
|
||||
List<BaiduPushResult> results = new ArrayList<>(publishPipes.size());
|
||||
publishPipes.forEach(pp -> {
|
||||
String domain = site.getUrl(pp.getCode());
|
||||
if (StringUtils.isEmpty(domain)) {
|
||||
return;
|
||||
}
|
||||
if (domain.contains("://")) {
|
||||
domain = StringUtils.substringAfter(domain, "://");
|
||||
}
|
||||
List<CmsContent> list = contentService.lambdaQuery().in(CmsContent::getContentId, contentIds).list();
|
||||
List<String> urls = list.stream().map(content -> contentService
|
||||
.getContentLink(content, 1, pp.getCode(), false)).toList();
|
||||
|
||||
String apiUrl = StringUtils.messageFormat(API, domain, secret);
|
||||
String body = StringUtils.join(urls, "\n");
|
||||
String response = HttpUtils.post(URI.create(apiUrl), body, Map.of("Content-Type", "text/plain"));
|
||||
BaiduPushResult r = JacksonUtils.from(response, BaiduPushResult.class);
|
||||
r.setPublishPipeCode(pp.getCode());
|
||||
results.add(r);
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class BaiduPushResult {
|
||||
|
||||
private String publishPipeCode;
|
||||
|
||||
private Integer remain;
|
||||
|
||||
private Integer success;
|
||||
|
||||
private List<String> not_same_site;
|
||||
|
||||
private List<String> not_valid;
|
||||
}
|
||||
}
|
||||
@ -5,4 +5,4 @@ SCHEDULED_TASK.SiteMapJobHandler=站点地图定时更新任务
|
||||
DICT.CMSSitemapPageType=发布通道页面类型
|
||||
DICT.CMSSitemapPageType.pc=PC端
|
||||
DICT.CMSSitemapPageType.mobile=移动端
|
||||
DICT.CMSSitemapPageType.pc_mobile=自适应
|
||||
DICT.CMSSitemapPageType.pc,mobile=自适应
|
||||
@ -5,4 +5,4 @@ SCHEDULED_TASK.SiteMapJobHandler=Sitemap Update Task
|
||||
DICT.CMSPublishPipePageType=发布通道页面类型
|
||||
DICT.CMSPublishPipePageType.pc=PC端
|
||||
DICT.CMSPublishPipePageType.mobile=移动端
|
||||
DICT.CMSPublishPipePageType.pc_mobile=自适应
|
||||
DICT.CMSPublishPipePageType.pc,mobile=自适应
|
||||
@ -5,4 +5,4 @@ SCHEDULED_TASK.SiteMapJobHandler=站點地圖定時更新任務
|
||||
DICT.CMSSitemapPageType=發布通道頁面類型
|
||||
DICT.CMSSitemapPageType.pc=PC端
|
||||
DICT.CMSSitemapPageType.mobile=移動端
|
||||
DICT.CMSSitemapPageType.pc_mobile=自適應
|
||||
DICT.CMSSitemapPageType.pc,mobile=自適應
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.chestnut</groupId>
|
||||
<artifactId>chestnut-cms</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chestnut-cms-stat</artifactId>
|
||||
|
||||
@ -31,7 +31,7 @@ public class CmsSiteVisitLog implements Serializable {
|
||||
|
||||
public final static String TABLE_NAME = "cms_site_visit_log";
|
||||
|
||||
@TableId(value = "log_id", type = IdType.AUTO)
|
||||
@TableId(value = "log_id", type = IdType.INPUT)
|
||||
private Long logId;
|
||||
|
||||
/**
|
||||
|
||||
@ -38,6 +38,8 @@ public class PageViewStatEventHandler implements IStatEventHandler {
|
||||
|
||||
public static final String TYPE = "pv";
|
||||
|
||||
static final String CACHE_PREFIX = "cms:stat:pv:";
|
||||
|
||||
private final AsyncTaskManager asyncTaskManager;
|
||||
|
||||
private final CmsSiteVisitLogMapper siteVisitLogMapper;
|
||||
@ -55,7 +57,7 @@ public class PageViewStatEventHandler implements IStatEventHandler {
|
||||
public void handle(StatEvent event) {
|
||||
CmsSiteVisitLog log = parseSiteVisitLog(event);
|
||||
// 更新Redis数据
|
||||
this.redisCache.incrCounter("cms:pv:" + log.getUri());
|
||||
this.redisCache.incrLongCounter(CACHE_PREFIX + log.getUri());
|
||||
// 更新内容浏览量
|
||||
if (IdUtils.validate(log.getContentId())) {
|
||||
contentDynamicDataService.increaseViewCount(log.getContentId());
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.chestnut</groupId>
|
||||
<artifactId>chestnut-cms</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chestnut-cms-vote</artifactId>
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.chestnut</groupId>
|
||||
<artifactId>chestnut-cms</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>chestnut-cms-word</artifactId>
|
||||
|
||||
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2022-2024 兮玥(190785909@qq.com)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.chestnut.cms.word.template.tag;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
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.StringUtils;
|
||||
import com.chestnut.word.domain.TagWordGroup;
|
||||
import com.chestnut.word.service.ITagWordGroupService;
|
||||
import freemarker.core.Environment;
|
||||
import freemarker.template.TemplateException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class CmsTagWordGroupTag extends AbstractListTag {
|
||||
|
||||
public final static String TAG_NAME = "cms_tag_word_group";
|
||||
public final static String NAME = "{FREEMARKER.TAG.NAME." + TAG_NAME + "}";
|
||||
public final static String DESC = "{FREEMARKER.TAG.DESC." + TAG_NAME + "}";
|
||||
|
||||
private static final String TAG_ATTR_CODE = "code";
|
||||
|
||||
private static final String TAG_ATTR_LEVEL = "level";
|
||||
|
||||
private final ITagWordGroupService tagWordGroupService;
|
||||
|
||||
@Override
|
||||
public List<TagAttr> getTagAttrs() {
|
||||
List<TagAttr> tagAttrs = super.getTagAttrs();
|
||||
tagAttrs.add(new TagAttr(TAG_ATTR_CODE, true, TagAttrDataType.STRING, "TAG词分组编码") );
|
||||
tagAttrs.add(new TagAttr(TAG_ATTR_LEVEL, false, TagAttrDataType.STRING, "数据获取范围,值为`Root`时忽略属性code",
|
||||
TagWordGroupTagLevel.toTagAttrOptions(), TagWordGroupTagLevel.Current.name()));
|
||||
return tagAttrs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagPageData prepareData(Environment env, Map<String, String> attrs, boolean page, int size, int pageIndex) throws TemplateException {
|
||||
String group = MapUtils.getString(attrs, TAG_ATTR_CODE);
|
||||
Optional<TagWordGroup> opt = tagWordGroupService.lambdaQuery().eq(TagWordGroup::getCode, group).oneOpt();
|
||||
if (opt.isEmpty()) {
|
||||
throw new TemplateException("Tag word group not found: " + group, env);
|
||||
}
|
||||
String level = MapUtils.getString(attrs, TAG_ATTR_LEVEL);
|
||||
|
||||
TagWordGroup parent = opt.get();
|
||||
LambdaQueryWrapper<TagWordGroup> q = new LambdaQueryWrapper<>();
|
||||
if (TagWordGroupTagLevel.isCurrent(level)) {
|
||||
q.eq(TagWordGroup::getParentId, parent.getParentId());
|
||||
} else if (TagWordGroupTagLevel.isChild(level)) {
|
||||
q.eq(TagWordGroup::getParentId, parent.getGroupId());
|
||||
}
|
||||
|
||||
String condition = MapUtils.getString(attrs, TagAttr.AttrName_Condition);
|
||||
q.apply(StringUtils.isNotEmpty(condition), condition);
|
||||
q.orderByAsc(TagWordGroup::getSortFlag);
|
||||
|
||||
Page<TagWordGroup> pageResult = this.tagWordGroupService.page(new Page<>(pageIndex, size, page), q);
|
||||
return TagPageData.of(pageResult.getRecords(), pageResult.getTotal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTagName() {
|
||||
return TAG_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return DESC;
|
||||
}
|
||||
|
||||
private enum TagWordGroupTagLevel {
|
||||
Root("所有分组"), Current("同级分组"), Child("子分组");
|
||||
|
||||
private final String desc;
|
||||
|
||||
TagWordGroupTagLevel(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
static boolean isRoot(String level) {
|
||||
return Root.name().equalsIgnoreCase(level);
|
||||
}
|
||||
|
||||
static boolean isCurrent(String level) {
|
||||
return Current.name().equalsIgnoreCase(level);
|
||||
}
|
||||
|
||||
static boolean isChild(String level) {
|
||||
return Child.name().equalsIgnoreCase(level);
|
||||
}
|
||||
|
||||
static List<TagAttrOption> toTagAttrOptions() {
|
||||
return List.of(
|
||||
new TagAttrOption(Root.name(), Root.desc),
|
||||
new TagAttrOption(Current.name(), Current.desc),
|
||||
new TagAttrOption(Child.name(), Child.desc)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,3 +5,5 @@ FREEMARKER.FUNC.DESC.replaceSensitiveWord=替换敏感词,例如:${replaceSe
|
||||
# freemarker模板标签
|
||||
FREEMARKER.TAG.NAME.cms_tag_word=TAG词列表标签
|
||||
FREEMARKER.TAG.DESC.cms_tag_word=根据TAG词分组编码获取TAG词列表,内嵌<#list DataList as tag>${tag.word}</#list>遍历数据
|
||||
FREEMARKER.TAG.NAME.cms_tag_word_group=TAG词分组列表标签
|
||||
FREEMARKER.TAG.DESC.cms_tag_word_group=根据TAG词分组编码获取TAG词分组列表,内嵌<#list DataList as group>${group.name}</#list>遍历数据
|
||||
@ -2,5 +2,7 @@
|
||||
FREEMARKER.FUNC.DESC.replaceHotWord=Replace hot word in content argument, eg: ${replaceHotWord(content, 'default', '[a href='\{0\}' target='\{2\}']\{1\}[/a]')}
|
||||
FREEMARKER.FUNC.DESC.replaceSensitiveWord=Replace sensitive word in content argument, eg: ${replaceSensitiveWord(content, 'xxx')}
|
||||
|
||||
FREEMARKER.TAG.NAME.cms_link=TAG Word List Tag
|
||||
FREEMARKER.TAG.DESC.cms_link=Fetch tag-word list, use <#list> in tag like "<#list DataList as tag>${tag.word}</#list>" to walk through the list of tag-words.
|
||||
FREEMARKER.TAG.NAME.cms_tag_word=TAG Word List Tag
|
||||
FREEMARKER.TAG.DESC.cms_tag_word=Fetch tag-word list, use <#list> in tag like "<#list DataList as tag>${tag.word}</#list>" to walk through the list of tag-words.
|
||||
FREEMARKER.TAG.NAME.cms_tag_word_group=TAG Word Group List Tag
|
||||
FREEMARKER.TAG.DESC.cms_tag_word_group=Fetch tag-word-group list, use <#list> in tag like "<#list DataList as group>${group.name}</#list>" to walk through the list of tag-word-groups.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user