diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/CatalogController.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/CatalogController.java index acc9ecf0..58db7296 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/CatalogController.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/controller/CatalogController.java @@ -44,6 +44,7 @@ import com.chestnut.contentcore.service.ICatalogService; import com.chestnut.contentcore.service.IPublishPipeService; import com.chestnut.contentcore.service.IPublishService; import com.chestnut.contentcore.service.ISiteService; +import com.chestnut.contentcore.user.preference.CatalogTreeExpandModePreference; import com.chestnut.contentcore.util.CmsPrivUtils; import com.chestnut.contentcore.util.ConfigPropertyUtils; import com.chestnut.contentcore.util.InternalUrlUtils; @@ -216,7 +217,11 @@ public class CatalogController extends BaseRestController { node.setDisabled(CatalogType_Link.ID.equals(catalog.getCatalogType())); } }); - return R.ok(Map.of("rows", treeData, "siteName", site.getName())); + return R.ok(Map.of( + "rows", treeData, + "siteName", site.getName(), + "expandMode", CatalogTreeExpandModePreference.getValue(loginUser)) + ); } /** diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/AbstractContent.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/AbstractContent.java index 4ba6bd45..882b62f1 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/AbstractContent.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/core/AbstractContent.java @@ -144,7 +144,7 @@ public abstract class AbstractContent implements IContent { content.setSiteId(catalog.getSiteId()); content.setCatalogAncestors(catalog.getAncestors()); content.setTopCatalog(CatalogUtils.getTopCatalog(catalog)); - content.setDeptId(Objects.nonNull(this.getOperator()) ? this.getOperator().getDeptId() : 0); + content.setDeptId(Objects.requireNonNullElse(this.getOperator().getDeptId(), 0L)); content.setContentType(this.getContentType()); content.setStatus(ContentStatus.DRAFT); diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/listener/event/AfterContentSaveEvent.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/listener/event/AfterContentSaveEvent.java index 4f65bd14..e1a32488 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/listener/event/AfterContentSaveEvent.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/listener/event/AfterContentSaveEvent.java @@ -35,8 +35,4 @@ public class AfterContentSaveEvent extends ApplicationEvent { this.content = content; this.add = add; } - - public boolean isAdd() { - return add; - } } diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/CatalogServiceImpl.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/CatalogServiceImpl.java index f7177eb4..333c07f2 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/CatalogServiceImpl.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/CatalogServiceImpl.java @@ -1,637 +1,636 @@ -/* - * 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.service.impl; - -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.chestnut.common.async.AsyncTask; -import com.chestnut.common.async.AsyncTaskManager; -import com.chestnut.common.domain.TreeNode; -import com.chestnut.common.exception.CommonErrorCode; -import com.chestnut.common.redis.RedisCache; -import com.chestnut.common.security.domain.LoginUser; -import com.chestnut.common.staticize.core.TemplateContext; -import com.chestnut.common.utils.*; -import com.chestnut.contentcore.ContentCoreConsts; -import com.chestnut.contentcore.config.CMSConfig; -import com.chestnut.contentcore.core.IInternalDataType; -import com.chestnut.contentcore.core.IProperty; -import com.chestnut.contentcore.core.InternalURL; -import com.chestnut.contentcore.core.impl.CatalogType_Common; -import com.chestnut.contentcore.core.impl.CatalogType_Link; -import com.chestnut.contentcore.core.impl.InternalDataType_Catalog; -import com.chestnut.contentcore.domain.CmsCatalog; -import com.chestnut.contentcore.domain.CmsContent; -import com.chestnut.contentcore.domain.CmsSite; -import com.chestnut.contentcore.domain.dto.*; -import com.chestnut.contentcore.exception.ContentCoreErrorCode; -import com.chestnut.contentcore.listener.event.AfterCatalogDeleteEvent; -import com.chestnut.contentcore.listener.event.AfterCatalogMoveEvent; -import com.chestnut.contentcore.listener.event.AfterCatalogSaveEvent; -import com.chestnut.contentcore.listener.event.BeforeCatalogDeleteEvent; -import com.chestnut.contentcore.mapper.CmsCatalogMapper; -import com.chestnut.contentcore.mapper.CmsContentMapper; -import com.chestnut.contentcore.perms.CatalogPermissionType; -import com.chestnut.contentcore.service.ICatalogService; -import com.chestnut.contentcore.service.ISiteService; -import com.chestnut.contentcore.util.*; -import com.chestnut.system.fixed.dict.YesOrNo; -import com.chestnut.system.service.ISysPermissionService; -import lombok.RequiredArgsConstructor; -import org.redisson.api.RLock; -import org.redisson.api.RedissonClient; -import org.springframework.beans.BeanUtils; -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; -import java.util.function.BiConsumer; -import java.util.stream.Collectors; - -@Service -@RequiredArgsConstructor -public class CatalogServiceImpl extends ServiceImpl implements ICatalogService { - - public static final String CACHE_PREFIX_ID = CMSConfig.CachePrefix + "catalog:id:"; - - public static final String CACHE_PREFIX_ALIAS = CMSConfig.CachePrefix + "catalog:alias:"; - - private final ApplicationContext applicationContext; - - private final ISiteService siteService; - - private final RedisCache redisCache; - - private final RedissonClient redissonClient; - - private final CmsContentMapper contentMapper; - - private final ISysPermissionService permissionService; - - private final AsyncTaskManager asyncTaskManager; - - @Override - public CmsCatalog getCatalog(Long catalogId) { - if (!IdUtils.validate(catalogId)) { - return null; - } - CmsCatalog catalog = this.redisCache.getCacheObject(CACHE_PREFIX_ID + catalogId); - if (Objects.isNull(catalog)) { - catalog = this.getById(catalogId); - if (Objects.nonNull(catalog)) { - this.setCatalogCache(catalog); - } - } - return catalog; - } - - @Override - public CmsCatalog getCatalogByAlias(Long siteId, String catalogAlias) { - if (!IdUtils.validate(siteId) || StringUtils.isEmpty(catalogAlias)) { - return null; - } - Assert.notNull(catalogAlias, () -> CommonErrorCode.NOT_EMPTY.exception("CatalogAlias: " + catalogAlias)); - CmsCatalog catalog = this.redisCache.getCacheObject(CACHE_PREFIX_ALIAS + siteId + ":" + catalogAlias); - if (Objects.isNull(catalog)) { - catalog = this.lambdaQuery().eq(CmsCatalog::getSiteId, siteId).eq(CmsCatalog::getAlias, catalogAlias).one(); - if (Objects.nonNull(catalog)) { - this.setCatalogCache(catalog); - } - } - return catalog; - } - - @Override - public boolean checkCatalogUnique(Long siteId, Long catalogId, String alias, String path) { - LambdaQueryWrapper q = new LambdaQueryWrapper().eq(CmsCatalog::getSiteId, siteId) - .and(wrapper -> wrapper.eq(CmsCatalog::getAlias, alias).or().eq(CmsCatalog::getPath, path)) - .ne(catalogId != null && catalogId > 0, CmsCatalog::getCatalogId, catalogId); - return this.count(q) == 0; - } - - @Override - public List> buildCatalogTreeData(List catalogs, BiConsumer> consumer) { - if (Objects.isNull(catalogs)) { - return List.of(); - } - List> list = catalogs.stream().map(c -> { - TreeNode treeNode = new TreeNode<>(String.valueOf(c.getCatalogId()), - String.valueOf(c.getParentId()), c.getName(), c.getParentId() == 0); - String internalUrl = InternalUrlUtils.getInternalUrl(InternalDataType_Catalog.ID, c.getCatalogId()); - String logoSrc = InternalUrlUtils.getActualPreviewUrl(c.getLogo()); - Map props = Map.of("path", c.getPath(), "internalUrl", internalUrl, "logo", - c.getLogo() == null ? "" : c.getLogo(), "logoSrc", logoSrc == null ? "" : logoSrc, "description", - c.getDescription() == null ? "" : c.getDescription()); - treeNode.setProps(props); - consumer.accept(c, treeNode); - return treeNode; - }).toList(); - return TreeNode.build(list); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public CmsCatalog addCatalog(CatalogAddDTO dto) { - dto.setPath(CatalogUtils.formatCatalogPath(dto.getPath())); - Assert.isFalse("/".equals(dto.getPath()), () -> CommonErrorCode.NOT_EMPTY.exception("path")); - - boolean checkCatalogUnique = this.checkCatalogUnique(dto.getSiteId(), null, dto.getAlias(), - dto.getPath()); - Assert.isTrue(checkCatalogUnique, ContentCoreErrorCode.CONFLICT_CATALOG::exception); - - if (dto.getParentId() == null) { - dto.setParentId(0L); - } - CmsCatalog catalog = new CmsCatalog(); - catalog.setCatalogId(IdUtils.getSnowflakeId()); - catalog.setSiteId(dto.getSiteId()); - catalog.setParentId(dto.getParentId()); - catalog.setName(dto.getName()); - catalog.setAlias(dto.getAlias()); - catalog.setPath(dto.getPath()); - catalog.setCatalogType(dto.getCatalogType()); - catalog.setTreeLevel(1); - String parentAncestors = StringUtils.EMPTY; - if (catalog.getParentId() > 0) { - CmsCatalog parentCatalog = this.getById(catalog.getParentId()); - boolean maxTreeLevelFlag = parentCatalog.getTreeLevel() + 1 <= ContentCoreConsts.CATALOG_MAX_TREE_LEVEL; - Assert.isTrue(maxTreeLevelFlag, ContentCoreErrorCode.CATALOG_MAX_TREE_LEVEL::exception); - - catalog.setTreeLevel(parentCatalog.getTreeLevel() + 1); - parentCatalog.setChildCount(parentCatalog.getChildCount() + 1); - this.updateById(parentCatalog); - - parentAncestors = parentCatalog.getAncestors(); - } - catalog.setAncestors(CatalogUtils.getCatalogAncestors(parentAncestors, catalog.getCatalogId())); - catalog.setSortFlag(SortUtils.getDefaultSortValue()); - catalog.setContentCount(0); - catalog.setChildCount(0); - catalog.setStaticFlag(YesOrNo.YES); - catalog.setVisibleFlag(YesOrNo.YES); - catalog.setTagIgnore(YesOrNo.NO); - catalog.createBy(dto.getOperator().getUsername()); - this.save(catalog); - // 授权给添加人 - this.permissionService.grantUserPermission( - dto.getOperator(), - CatalogPermissionType.ID, - CmsPrivUtils.getAllCatalogPermissions(catalog.getCatalogId()) - ); - return catalog; - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void batchAddCatalog(CatalogBatchAddDTO dto) { - CmsCatalog rootParent = null; - if (IdUtils.validate(dto.getParentId())) { - rootParent = getCatalog(dto.getParentId()); - } - List list = this.lambdaQuery() - .select(CmsCatalog::getAlias, CmsCatalog::getPath) - .eq(CmsCatalog::getSiteId, dto.getSiteId()) - .list(); - Set aliasSet = list.stream().map(CmsCatalog::getAlias).collect(Collectors.toSet()); - Set pathSet = list.stream().map(CmsCatalog::getPath).collect(Collectors.toSet()); - - List catalogs = new ArrayList<>(); - String[] arr = dto.getCatalogs().split("\n"); - Map lastTreeLevelCatalogs = new HashMap<>(); - for (String item : arr) { - CmsCatalog catalog = new CmsCatalog(); - catalog.setCatalogId(IdUtils.getSnowflakeId()); - catalog.setName(item.trim()); - catalog.setParentId(0L); - catalog.setTreeLevel(1); - catalog.setAlias(ChineseSpelling.getCapitalizedSpelling(catalog.getName()).toLowerCase()); - catalog.setPath(catalog.getAlias() + StringUtils.SLASH); - catalog.setChildCount(0); - - int treeLevel = StringUtils.countMatches(item, " ") / 2 + 1; - CmsCatalog parent = rootParent; - if (treeLevel > 1) { - parent = lastTreeLevelCatalogs.get(treeLevel - 1); - } - if (Objects.nonNull(parent)) { - catalog.setParentId(parent.getCatalogId()); - catalog.setTreeLevel(parent.getTreeLevel() + 1) ; - - catalog.setAlias(parent.getAlias() + StringUtils.Underline + catalog.getAlias()); - catalog.setPath(parent.getPath() + catalog.getPath()); - - parent.setChildCount(parent.getChildCount() + 1); - } - int index = 1; - while(aliasSet.contains(catalog.getAlias())) { - index++; - catalog.setAlias(catalog.getAlias() + index); - } - index = 1; - if (pathSet.contains(catalog.getPath())) { - index++; - String path = StringUtils.substringBeforeLast(catalog.getPath(), StringUtils.SLASH); - catalog.setPath(path + index + StringUtils.SLASH); - } - catalog.setAncestors(CatalogUtils.getCatalogAncestors(parent, catalog.getCatalogId())); - catalog.setSiteId(dto.getSiteId()); - catalog.setCatalogType(CatalogType_Common.ID); - catalog.setSortFlag(SortUtils.getDefaultSortValue()); - catalog.setContentCount(0); - catalog.setStaticFlag(YesOrNo.YES); - catalog.setVisibleFlag(YesOrNo.YES); - catalog.setTagIgnore(YesOrNo.NO); - catalog.createBy(dto.getOperator().getUsername()); - catalogs.add(catalog); - - lastTreeLevelCatalogs.put(treeLevel, catalog); - aliasSet.add(catalog.getAlias()); - pathSet.add(catalog.getPath()); - } - - if (saveBatch(catalogs)) { - // 授权给添加人 - catalogs.forEach(catalog -> { - this.permissionService.grantUserPermission( - dto.getOperator(), - CatalogPermissionType.ID, - CmsPrivUtils.getAllCatalogPermissions(catalog.getCatalogId()) - ); - }); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public CmsCatalog editCatalog(CatalogUpdateDTO dto) { - dto.setPath(CatalogUtils.formatCatalogPath(dto.getPath())); - Assert.isFalse("/".equals(dto.getPath()), () -> CommonErrorCode.NOT_EMPTY.exception("path")); - - CmsCatalog catalog = this.getById(dto.getCatalogId()); - Assert.notNull(catalog, () -> CommonErrorCode.DATA_NOT_FOUND_BY_ID.exception("catalogId", dto.getCatalogId())); - - boolean checkCatalogUnique = this.checkCatalogUnique(catalog.getSiteId(), catalog.getCatalogId(), - dto.getAlias(), dto.getPath()); - Assert.isTrue(checkCatalogUnique, ContentCoreErrorCode.CONFLICT_CATALOG::exception); - // 校验内链 - checkRedirectUrl(dto.getCatalogType(), dto.getRedirectUrl()); - - String oldPath = catalog.getPath(); - BeanUtils.copyProperties(dto, catalog); - // 发布通道数据处理 - Map> publishPipeProps = dto.getPublishPipeDatas().stream() - .collect(Collectors.toMap(PublishPipeProp::getPipeCode, PublishPipeProp::getProps)); - catalog.setPublishPipeProps(publishPipeProps); - catalog.updateBy(dto.getOperator().getUsername()); - this.updateById(catalog); - - this.clearCache(catalog); - this.applicationContext.publishEvent(new AfterCatalogSaveEvent(this, catalog, oldPath, dto.getParams())); - return catalog; - } - - void checkRedirectUrl(String catalogType, String redirectUrl) { - if (CatalogType_Link.ID.equals(catalogType)) { - // 校验redirectUrl是否是链接到了内部链接数据 - InternalURL internalURL = InternalUrlUtils.parseInternalUrl(redirectUrl); - if (Objects.nonNull(internalURL)) { - IInternalDataType idt = ContentCoreUtils.getInternalDataType(internalURL.getType()); - Assert.isFalse(idt.isLinkData(internalURL.getId()), - ContentCoreErrorCode.DENY_LINK_TO_LINK_INTERNAL_DATA::exception); - } - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public CmsCatalog deleteCatalog(long catalogId, LoginUser operator) { - CmsCatalog catalog = this.getById(catalogId); - applicationContext.publishEvent(new BeforeCatalogDeleteEvent(this, catalog, operator)); - - AsyncTaskManager.setTaskMessage("正在删除栏目数据"); - long childCount = lambdaQuery().eq(CmsCatalog::getParentId, catalog.getCatalogId()).count(); - Assert.isTrue(childCount == 0, ContentCoreErrorCode.DEL_CHILD_FIRST::exception); - - if (catalog.getParentId() > 0) { - CmsCatalog parentCatalog = getById(catalog.getParentId()); - parentCatalog.setChildCount(parentCatalog.getChildCount() - 1); - updateById(parentCatalog); - } - // 删除栏目 - removeById(catalogId); - // 清除缓存 - clearCache(catalog); - applicationContext.publishEvent(new AfterCatalogDeleteEvent(this, catalog)); - return catalog; - } - - @Override - public String getCatalogLink(CmsCatalog catalog, int pageIndex, String publishPipeCode, boolean isPreview) { - CmsSite site = this.siteService.getSite(catalog.getSiteId()); - return CatalogUtils.getCatalogLink(site, catalog, pageIndex, publishPipeCode, isPreview); - } - - @Override - public String getCatalogListLink(CmsCatalog catalog, int pageIndex, String publishPipeCode, boolean isPreview) { - CmsSite site = this.siteService.getSite(catalog.getSiteId()); - return CatalogUtils.getCatalogListLink(site, catalog, pageIndex, publishPipeCode, isPreview); - } - - @Override - public void applyConfigPropsToChildren(CatalogApplyConfigPropsDTO dto) { - CmsCatalog catalog = this.getCatalog(dto.getCatalogId()); - - LambdaQueryWrapper q = new LambdaQueryWrapper<>(); - if (StringUtils.isEmpty(dto.getToCatalogIds())) { - q.likeRight(CmsCatalog::getAncestors, catalog.getAncestors() + CatalogUtils.ANCESTORS_SPLITER); - } else { - q.in(CmsCatalog::getCatalogId, dto.getToCatalogIds()); - } - List toCatalogs = this.list(q); - for (CmsCatalog toCatalog : toCatalogs) { - if (dto.isAllExtends()) { - toCatalog.setConfigProps(catalog.getConfigProps()); - } else if (StringUtils.isNotEmpty(dto.getConfigPropKeys())) { - dto.getConfigPropKeys().forEach(propKey -> { - toCatalog.getConfigProps().put(propKey, catalog.getConfigProps().get(propKey)); - }); - } - toCatalog.updateBy(dto.getOperator().getUsername()); - } - this.updateBatchById(toCatalogs); - toCatalogs.forEach(this::clearCache); - } - - @Override - public void applyPublishPipePropsToChildren(CatalogApplyPublishPipeDTO dto) { - CmsCatalog catalog = this.getCatalog(dto.getCatalogId()); - - LambdaQueryWrapper q = new LambdaQueryWrapper<>(); - q.likeRight(CmsCatalog::getAncestors, catalog.getAncestors() + CatalogUtils.ANCESTORS_SPLITER); - List toCatalogs = this.list(q); - for (CmsCatalog toCatalog : toCatalogs) { - Map publishPipeProps = toCatalog.getPublishPipeProps(dto.getPublishPipeCode()); - for (String propKey : dto.getPublishPipePropKeys()) { - publishPipeProps.put(propKey, catalog.getPublishPipeProps(dto.getPublishPipeCode()).get(propKey)); - } - toCatalog.updateBy(dto.getOperator().getUsername()); - } - this.updateBatchById(toCatalogs); - toCatalogs.forEach(this::clearCache); - } - - @Override - public void applySiteDefaultTemplateToCatalog(SiteDefaultTemplateDTO dto) { - CmsSite site = this.siteService.getSite(dto.getSiteId()); - - LambdaQueryWrapper q = new LambdaQueryWrapper().in(CmsCatalog::getCatalogId, - dto.getToCatalogIds()); - List toCatalogs = this.list(q); - if (!toCatalogs.isEmpty()) { - for (CmsCatalog toCatalog : toCatalogs) { - List publishPipeProps = dto.getPublishPipeProps(); - for (PublishPipeProp publishPipeProp : publishPipeProps) { - Map sitePublishPipeProp = site.getPublishPipeProps() - .get(publishPipeProp.getPipeCode()); - Map catalogPublishPipeProp = toCatalog - .getPublishPipeProps(publishPipeProp.getPipeCode()); - publishPipeProp.getProps().keySet().forEach(key -> { - catalogPublishPipeProp.put(key, sitePublishPipeProp.get(key)); - }); - } - toCatalog.updateBy(dto.getOperator().getUsername()); - } - this.updateBatchById(toCatalogs); - } - } - - @Override - public void clearCache(CmsCatalog catalog) { - this.redisCache.deleteObject(CACHE_PREFIX_ID + catalog.getCatalogId()); - this.redisCache.deleteObject(CACHE_PREFIX_ALIAS + catalog.getSiteId() + ":" + catalog.getAlias()); - } - - private void setCatalogCache(CmsCatalog catalog) { - this.redisCache.setCacheObject(CACHE_PREFIX_ID + catalog.getCatalogId(), catalog); - this.redisCache.setCacheObject(CACHE_PREFIX_ALIAS + catalog.getSiteId() + ":" + catalog.getAlias(), catalog); - } - - @Override - public void changeVisible(Long catalogId, String visible) { - CmsCatalog catalog = this.getCatalog(catalogId); - if (StringUtils.equals(visible, catalog.getVisibleFlag())) { - return; - } - catalog.setVisibleFlag(YesOrNo.isYes(visible) ? YesOrNo.YES : YesOrNo.NO); - this.updateById(catalog); - this.clearCache(catalog); - } - - @Override - public AsyncTask moveCatalog(CmsCatalog fromCatalog, CmsCatalog toCatalog) { - // 所有需要迁移的子栏目,按Ancestors排序依次处理 - List children = this.lambdaQuery().ne(CmsCatalog::getCatalogId, fromCatalog.getCatalogId()) - .likeRight(CmsCatalog::getAncestors, fromCatalog.getAncestors()).orderByAsc(CmsCatalog::getAncestors) - .list(); - // 判断栏目ancestors长度是否会超过限制 - int baseTreeLevel = Objects.isNull(toCatalog) ? 1 : toCatalog.getTreeLevel() + 1; - int maxTreelevel = baseTreeLevel; - for (CmsCatalog catalog : children) { - maxTreelevel = Math.max(maxTreelevel, catalog.getTreeLevel() - fromCatalog.getTreeLevel() + baseTreeLevel); - } - Assert.isTrue(ContentCoreConsts.CATALOG_MAX_TREE_LEVEL >= maxTreelevel, - ContentCoreErrorCode.CATALOG_MAX_TREE_LEVEL::exception); - AsyncTask task = new AsyncTask() { - - @Override - public void run0() { - moveCatalog0(fromCatalog, toCatalog, children); - } - }; - // 设置唯一任务ID避免同步执行,可能会导致数据错乱。 - task.setTaskId("CatalogMove"); - this.asyncTaskManager.execute(task); - return task; - } - - @Transactional(rollbackFor = Throwable.class) - private void moveCatalog0(CmsCatalog fromCatalog, CmsCatalog toCatalog, List children) { - Map invokedCatalogs = new HashMap<>(); - AsyncTaskManager.setTaskPercent(10); - // 1、原父级栏目子节点数-1 - if (fromCatalog.getParentId() > 0) { - AsyncTaskManager.setTaskMessage("更新转移栏目原父级栏目数据"); - CmsCatalog parent = this.getById(fromCatalog.getParentId()); - parent.setChildCount(parent.getChildCount() - 1); - invokedCatalogs.put(parent.getCatalogId(), parent); - } - // 2、来源栏目修改相关属性 - AsyncTaskManager.setTaskMessage("更新转移栏目数据"); - fromCatalog.setParentId(Objects.isNull(toCatalog) ? 0 : toCatalog.getCatalogId()); - String fromCatalogAncestors = CatalogUtils.getCatalogAncestors(toCatalog, fromCatalog.getCatalogId()); - fromCatalog.setAncestors(fromCatalogAncestors); - fromCatalog.setTreeLevel(Objects.isNull(toCatalog) ? 1 : toCatalog.getTreeLevel() + 1); - fromCatalog.setSortFlag(SortUtils.getDefaultSortValue()); - invokedCatalogs.put(fromCatalog.getCatalogId(), fromCatalog); - // 3、依次处理所有栏目 - for (CmsCatalog child : children) { - AsyncTaskManager.setTaskMessage("更新转移栏目子栏目数据"); - CmsCatalog parent = invokedCatalogs.get(child.getParentId()); - child.setAncestors(CatalogUtils.getCatalogAncestors(parent.getAncestors(), child.getCatalogId())); - child.setTreeLevel(parent.getTreeLevel() + 1); - invokedCatalogs.put(child.getCatalogId(), child); - } - // 4、目标栏目子栏目数+1 - if (Objects.nonNull(toCatalog)) { - AsyncTaskManager.setTaskMessage("更新转移目标栏目数据"); - toCatalog.setChildCount(toCatalog.getChildCount() + 1); - invokedCatalogs.put(toCatalog.getCatalogId(), toCatalog); - } - AsyncTaskManager.setTaskPercent(20); - // 5、批量更新数据库 - invokedCatalogs.values().forEach(catalog -> { - this.lambdaUpdate().set(CmsCatalog::getParentId, catalog.getParentId()) - .set(CmsCatalog::getAncestors, catalog.getAncestors()) - .set(CmsCatalog::getTreeLevel, catalog.getTreeLevel()) - .set(CmsCatalog::getChildCount, catalog.getChildCount()) - .eq(CmsCatalog::getCatalogId, catalog.getCatalogId()).update(); - }); - AsyncTaskManager.setTaskPercent(30); - // 6、更新缓存 - invokedCatalogs.values().forEach(this::clearCache); - // 7、更新除目标栏目外的所有栏目内容数据 - AsyncTaskManager.setTaskProgressInfo(40, "更新转移栏目及其子栏目内容"); - invokedCatalogs.values().forEach(catalog -> { - if (toCatalog == null || !Objects.equals(catalog.getCatalogId(), toCatalog.getCatalogId())) { - new LambdaUpdateChainWrapper<>(contentMapper) - .set(CmsContent::getCatalogAncestors, catalog.getAncestors()) - .eq(CmsContent::getCatalogId, catalog.getCatalogId()).update(); - } - }); - // 8、其他扩展,例如:重建栏目内容索引 - this.applicationContext.publishEvent(new AfterCatalogMoveEvent(this, fromCatalog, toCatalog, children)); - } - - @Override - public void setStaticPath(CmsCatalog catalog, TemplateContext context, boolean hasIndex) { - CmsSite site = this.siteService.getSite(catalog.getSiteId()); - String siteRoot = SiteUtils.getSiteRoot(site, context.getPublishPipeCode()); - context.setDirectory(siteRoot + catalog.getPath()); - String suffix = site.getStaticSuffix(context.getPublishPipeCode()); - String name = hasIndex ? "list" : "index"; - context.setFirstFileName(name + StringUtils.DOT + suffix); - context.setOtherFileName(name + "_" + TemplateContext.PlaceHolder_PageNo + StringUtils.DOT + suffix); - } - - @Override - public void saveCatalogExtends(Long catalogId, Map configs, String operator) { - CmsCatalog catalog = this.getCatalog(catalogId); - ConfigPropertyUtils.filterConfigProps(configs, catalog.getConfigProps(), IProperty.UseType.Catalog); - - catalog.setConfigProps(configs); - catalog.updateBy(operator); - this.updateById(catalog); - this.clearCache(catalog); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void sortCatalog(Long catalogId, Integer sort) { - CmsCatalog catalog = this.getCatalog(catalogId); - if (sort < 0) { - // 上移 - List beforeCatalogs = this.lambdaQuery() - .select(List.of(CmsCatalog::getSiteId, CmsCatalog::getCatalogId, CmsCatalog::getAlias, - CmsCatalog::getSortFlag)) - .eq(CmsCatalog::getSiteId, catalog.getSiteId()).eq(CmsCatalog::getParentId, catalog.getParentId()) - .lt(CmsCatalog::getSortFlag, catalog.getSortFlag()).orderByDesc(CmsCatalog::getSortFlag) - .page(new Page<>(1, Math.abs(sort), false)).getRecords(); - if (beforeCatalogs.isEmpty()) { - return; // 无需排序 - } - CmsCatalog targetCatalog = beforeCatalogs.get(beforeCatalogs.size() - 1); - // 更新排序值 - this.catalogSortPlusOne(targetCatalog.getSortFlag(), catalog.getSortFlag()); - this.lambdaUpdate().set(CmsCatalog::getSortFlag, targetCatalog.getSortFlag()) - .eq(CmsCatalog::getCatalogId, catalog.getCatalogId()).update(); - beforeCatalogs.forEach(this::clearCache); - } else { - // 下移 - List afterCatalogs = this.lambdaQuery() - .select(List.of(CmsCatalog::getSiteId, CmsCatalog::getCatalogId, CmsCatalog::getAlias, - CmsCatalog::getSortFlag)) - .eq(CmsCatalog::getSiteId, catalog.getSiteId()).eq(CmsCatalog::getParentId, catalog.getParentId()) - .gt(CmsCatalog::getSortFlag, catalog.getSortFlag()).orderByAsc(CmsCatalog::getSortFlag) - .page(new Page<>(1, sort, false)).getRecords(); - if (afterCatalogs.isEmpty()) { - return; // 无需排序 - } - CmsCatalog targetCatalog = afterCatalogs.get(afterCatalogs.size() - 1); - // 更新排序值 - this.catalogSortMinusOne(catalog.getSortFlag(), targetCatalog.getSortFlag()); - this.lambdaUpdate().set(CmsCatalog::getSortFlag, targetCatalog.getSortFlag()) - .eq(CmsCatalog::getCatalogId, catalog.getCatalogId()).update(); - afterCatalogs.forEach(this::clearCache); - } - this.clearCache(catalog); - } - - /** - * 排序标识范围内[startSort, endSort)的所有栏目排序值+1 - */ - private void catalogSortPlusOne(long startSort, long endSort) { - List catalogs = this.lambdaQuery() - .select(CmsCatalog::getCatalogId, CmsCatalog::getSortFlag) - .ge(CmsCatalog::getSortFlag, startSort) - .lt(CmsCatalog::getSortFlag, endSort) - .list(); - catalogs.forEach(catalog -> { - this.lambdaUpdate().set(CmsCatalog::getSortFlag, catalog.getSortFlag() + 1) - .eq(CmsCatalog::getCatalogId, catalog.getCatalogId()).update(); - }); - } - - /** - * 排序标识范围内(startSort, endSort]的所有栏目排序值-1 - */ - private void catalogSortMinusOne(long startSort, long endSort) { - List catalogs = this.lambdaQuery() - .select(CmsCatalog::getCatalogId, CmsCatalog::getSortFlag) - .gt(CmsCatalog::getSortFlag, startSort) - .le(CmsCatalog::getSortFlag, endSort) - .list(); - catalogs.forEach(catalog -> { - this.lambdaUpdate().set(CmsCatalog::getSortFlag, catalog.getSortFlag() - 1) - .eq(CmsCatalog::getCatalogId, catalog.getCatalogId()).update(); - }); - } - - @Override - public void changeContentCount(Long catalogId, int delta) { - RLock lock = redissonClient.getLock("Catalog-" + catalogId); - lock.lock(); - try { - CmsCatalog catalog = getById(catalogId); - lambdaUpdate().set(CmsCatalog::getContentCount, catalog.getContentCount() + delta) - .eq(CmsCatalog::getCatalogId, catalog.getCatalogId()).update(); - } finally { - lock.unlock(); - } - } -} +/* + * 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.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.chestnut.common.async.AsyncTask; +import com.chestnut.common.async.AsyncTaskManager; +import com.chestnut.common.domain.TreeNode; +import com.chestnut.common.exception.CommonErrorCode; +import com.chestnut.common.redis.RedisCache; +import com.chestnut.common.security.domain.LoginUser; +import com.chestnut.common.staticize.core.TemplateContext; +import com.chestnut.common.utils.*; +import com.chestnut.contentcore.ContentCoreConsts; +import com.chestnut.contentcore.config.CMSConfig; +import com.chestnut.contentcore.core.IInternalDataType; +import com.chestnut.contentcore.core.IProperty; +import com.chestnut.contentcore.core.InternalURL; +import com.chestnut.contentcore.core.impl.CatalogType_Common; +import com.chestnut.contentcore.core.impl.CatalogType_Link; +import com.chestnut.contentcore.core.impl.InternalDataType_Catalog; +import com.chestnut.contentcore.domain.CmsCatalog; +import com.chestnut.contentcore.domain.CmsContent; +import com.chestnut.contentcore.domain.CmsSite; +import com.chestnut.contentcore.domain.dto.*; +import com.chestnut.contentcore.exception.ContentCoreErrorCode; +import com.chestnut.contentcore.listener.event.AfterCatalogDeleteEvent; +import com.chestnut.contentcore.listener.event.AfterCatalogMoveEvent; +import com.chestnut.contentcore.listener.event.AfterCatalogSaveEvent; +import com.chestnut.contentcore.listener.event.BeforeCatalogDeleteEvent; +import com.chestnut.contentcore.mapper.CmsCatalogMapper; +import com.chestnut.contentcore.mapper.CmsContentMapper; +import com.chestnut.contentcore.perms.CatalogPermissionType; +import com.chestnut.contentcore.service.ICatalogService; +import com.chestnut.contentcore.service.ISiteService; +import com.chestnut.contentcore.util.*; +import com.chestnut.system.fixed.dict.YesOrNo; +import com.chestnut.system.service.ISysPermissionService; +import lombok.RequiredArgsConstructor; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; +import org.springframework.beans.BeanUtils; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class CatalogServiceImpl extends ServiceImpl implements ICatalogService { + + public static final String CACHE_PREFIX_ID = CMSConfig.CachePrefix + "catalog:id:"; + + public static final String CACHE_PREFIX_ALIAS = CMSConfig.CachePrefix + "catalog:alias:"; + + private final ApplicationContext applicationContext; + + private final ISiteService siteService; + + private final RedisCache redisCache; + + private final RedissonClient redissonClient; + + private final CmsContentMapper contentMapper; + + private final ISysPermissionService permissionService; + + private final AsyncTaskManager asyncTaskManager; + + @Override + public CmsCatalog getCatalog(Long catalogId) { + if (!IdUtils.validate(catalogId)) { + return null; + } + CmsCatalog catalog = this.redisCache.getCacheObject(CACHE_PREFIX_ID + catalogId); + if (Objects.isNull(catalog)) { + catalog = this.getById(catalogId); + if (Objects.nonNull(catalog)) { + this.setCatalogCache(catalog); + } + } + return catalog; + } + + @Override + public CmsCatalog getCatalogByAlias(Long siteId, String catalogAlias) { + if (!IdUtils.validate(siteId) || StringUtils.isEmpty(catalogAlias)) { + return null; + } + Assert.notNull(catalogAlias, () -> CommonErrorCode.NOT_EMPTY.exception("CatalogAlias: " + catalogAlias)); + CmsCatalog catalog = this.redisCache.getCacheObject(CACHE_PREFIX_ALIAS + siteId + ":" + catalogAlias); + if (Objects.isNull(catalog)) { + catalog = this.lambdaQuery().eq(CmsCatalog::getSiteId, siteId).eq(CmsCatalog::getAlias, catalogAlias).one(); + if (Objects.nonNull(catalog)) { + this.setCatalogCache(catalog); + } + } + return catalog; + } + + @Override + public boolean checkCatalogUnique(Long siteId, Long catalogId, String alias, String path) { + LambdaQueryWrapper q = new LambdaQueryWrapper().eq(CmsCatalog::getSiteId, siteId) + .and(wrapper -> wrapper.eq(CmsCatalog::getAlias, alias).or().eq(CmsCatalog::getPath, path)) + .ne(catalogId != null && catalogId > 0, CmsCatalog::getCatalogId, catalogId); + return this.count(q) == 0; + } + + @Override + public List> buildCatalogTreeData(List catalogs, BiConsumer> consumer) { + if (Objects.isNull(catalogs)) { + return List.of(); + } + List> list = catalogs.stream().map(c -> { + TreeNode treeNode = new TreeNode<>(String.valueOf(c.getCatalogId()), + String.valueOf(c.getParentId()), c.getName(), c.getParentId() == 0); + String internalUrl = InternalUrlUtils.getInternalUrl(InternalDataType_Catalog.ID, c.getCatalogId()); + String logoSrc = InternalUrlUtils.getActualPreviewUrl(c.getLogo()); + Map props = Map.of("path", c.getPath(), "internalUrl", internalUrl, "logo", + c.getLogo() == null ? "" : c.getLogo(), "logoSrc", logoSrc == null ? "" : logoSrc, "description", + c.getDescription() == null ? "" : c.getDescription()); + treeNode.setProps(props); + consumer.accept(c, treeNode); + return treeNode; + }).toList(); + return TreeNode.build(list); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public CmsCatalog addCatalog(CatalogAddDTO dto) { + dto.setPath(CatalogUtils.formatCatalogPath(dto.getPath())); + Assert.isFalse("/".equals(dto.getPath()), () -> CommonErrorCode.NOT_EMPTY.exception("path")); + + boolean checkCatalogUnique = this.checkCatalogUnique(dto.getSiteId(), null, dto.getAlias(), + dto.getPath()); + Assert.isTrue(checkCatalogUnique, ContentCoreErrorCode.CONFLICT_CATALOG::exception); + + if (dto.getParentId() == null) { + dto.setParentId(0L); + } + CmsCatalog catalog = new CmsCatalog(); + catalog.setCatalogId(IdUtils.getSnowflakeId()); + catalog.setSiteId(dto.getSiteId()); + catalog.setParentId(dto.getParentId()); + catalog.setName(dto.getName()); + catalog.setAlias(dto.getAlias()); + catalog.setPath(dto.getPath()); + catalog.setCatalogType(dto.getCatalogType()); + catalog.setTreeLevel(1); + String parentAncestors = StringUtils.EMPTY; + if (catalog.getParentId() > 0) { + CmsCatalog parentCatalog = this.getById(catalog.getParentId()); + boolean maxTreeLevelFlag = parentCatalog.getTreeLevel() + 1 <= ContentCoreConsts.CATALOG_MAX_TREE_LEVEL; + Assert.isTrue(maxTreeLevelFlag, ContentCoreErrorCode.CATALOG_MAX_TREE_LEVEL::exception); + + catalog.setTreeLevel(parentCatalog.getTreeLevel() + 1); + parentCatalog.setChildCount(parentCatalog.getChildCount() + 1); + this.updateById(parentCatalog); + + parentAncestors = parentCatalog.getAncestors(); + } + catalog.setAncestors(CatalogUtils.getCatalogAncestors(parentAncestors, catalog.getCatalogId())); + catalog.setSortFlag(SortUtils.getDefaultSortValue()); + catalog.setContentCount(0); + catalog.setChildCount(0); + catalog.setStaticFlag(YesOrNo.YES); + catalog.setVisibleFlag(YesOrNo.YES); + catalog.setTagIgnore(YesOrNo.NO); + catalog.createBy(dto.getOperator().getUsername()); + this.save(catalog); + // 授权给添加人 + this.permissionService.grantUserPermission( + dto.getOperator(), + CatalogPermissionType.ID, + CmsPrivUtils.getAllCatalogPermissions(catalog.getCatalogId()) + ); + return catalog; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void batchAddCatalog(CatalogBatchAddDTO dto) { + CmsCatalog rootParent = null; + if (IdUtils.validate(dto.getParentId())) { + rootParent = getCatalog(dto.getParentId()); + } + List list = this.lambdaQuery() + .select(CmsCatalog::getAlias, CmsCatalog::getPath) + .eq(CmsCatalog::getSiteId, dto.getSiteId()) + .list(); + Set aliasSet = list.stream().map(CmsCatalog::getAlias).collect(Collectors.toSet()); + Set pathSet = list.stream().map(CmsCatalog::getPath).collect(Collectors.toSet()); + + List catalogs = new ArrayList<>(); + String[] arr = dto.getCatalogs().split("\n"); + Map lastTreeLevelCatalogs = new HashMap<>(); + for (String item : arr) { + CmsCatalog catalog = new CmsCatalog(); + catalog.setCatalogId(IdUtils.getSnowflakeId()); + catalog.setName(item.trim()); + catalog.setParentId(0L); + catalog.setTreeLevel(1); + catalog.setAlias(ChineseSpelling.getCapitalizedSpelling(catalog.getName()).toLowerCase()); + catalog.setPath(catalog.getAlias() + StringUtils.SLASH); + catalog.setChildCount(0); + + int treeLevel = StringUtils.countMatches(item, " ") / 2 + 1; + CmsCatalog parent = rootParent; + if (treeLevel > 1) { + parent = lastTreeLevelCatalogs.get(treeLevel - 1); + } + if (Objects.nonNull(parent)) { + catalog.setParentId(parent.getCatalogId()); + catalog.setTreeLevel(parent.getTreeLevel() + 1) ; + + catalog.setAlias(parent.getAlias() + StringUtils.Underline + catalog.getAlias()); + catalog.setPath(parent.getPath() + catalog.getPath()); + + parent.setChildCount(parent.getChildCount() + 1); + } + int index = 1; + while(aliasSet.contains(catalog.getAlias())) { + index++; + catalog.setAlias(catalog.getAlias() + index); + } + index = 1; + if (pathSet.contains(catalog.getPath())) { + index++; + String path = StringUtils.substringBeforeLast(catalog.getPath(), StringUtils.SLASH); + catalog.setPath(path + index + StringUtils.SLASH); + } + catalog.setAncestors(CatalogUtils.getCatalogAncestors(parent, catalog.getCatalogId())); + catalog.setSiteId(dto.getSiteId()); + catalog.setCatalogType(CatalogType_Common.ID); + catalog.setSortFlag(SortUtils.getDefaultSortValue()); + catalog.setContentCount(0); + catalog.setStaticFlag(YesOrNo.YES); + catalog.setVisibleFlag(YesOrNo.YES); + catalog.setTagIgnore(YesOrNo.NO); + catalog.createBy(dto.getOperator().getUsername()); + catalogs.add(catalog); + + lastTreeLevelCatalogs.put(treeLevel, catalog); + aliasSet.add(catalog.getAlias()); + pathSet.add(catalog.getPath()); + } + + if (saveBatch(catalogs)) { + // 授权给添加人 + catalogs.forEach(catalog -> { + this.permissionService.grantUserPermission( + dto.getOperator(), + CatalogPermissionType.ID, + CmsPrivUtils.getAllCatalogPermissions(catalog.getCatalogId()) + ); + }); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public CmsCatalog editCatalog(CatalogUpdateDTO dto) { + dto.setPath(CatalogUtils.formatCatalogPath(dto.getPath())); + Assert.isFalse("/".equals(dto.getPath()), () -> CommonErrorCode.NOT_EMPTY.exception("path")); + + CmsCatalog catalog = this.getById(dto.getCatalogId()); + Assert.notNull(catalog, () -> CommonErrorCode.DATA_NOT_FOUND_BY_ID.exception("catalogId", dto.getCatalogId())); + + boolean checkCatalogUnique = this.checkCatalogUnique(catalog.getSiteId(), catalog.getCatalogId(), + dto.getAlias(), dto.getPath()); + Assert.isTrue(checkCatalogUnique, ContentCoreErrorCode.CONFLICT_CATALOG::exception); + // 校验内链 + checkRedirectUrl(dto.getCatalogType(), dto.getRedirectUrl()); + + String oldPath = catalog.getPath(); + BeanUtils.copyProperties(dto, catalog); + // 发布通道数据处理 + Map> publishPipeProps = dto.getPublishPipeDatas().stream() + .collect(Collectors.toMap(PublishPipeProp::getPipeCode, PublishPipeProp::getProps)); + catalog.setPublishPipeProps(publishPipeProps); + catalog.updateBy(dto.getOperator().getUsername()); + this.updateById(catalog); + + this.clearCache(catalog); + this.applicationContext.publishEvent(new AfterCatalogSaveEvent(this, catalog, oldPath, dto.getParams())); + return catalog; + } + + void checkRedirectUrl(String catalogType, String redirectUrl) { + if (CatalogType_Link.ID.equals(catalogType)) { + // 校验redirectUrl是否是链接到了内部链接数据 + InternalURL internalURL = InternalUrlUtils.parseInternalUrl(redirectUrl); + if (Objects.nonNull(internalURL)) { + IInternalDataType idt = ContentCoreUtils.getInternalDataType(internalURL.getType()); + Assert.isFalse(idt.isLinkData(internalURL.getId()), + ContentCoreErrorCode.DENY_LINK_TO_LINK_INTERNAL_DATA::exception); + } + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public CmsCatalog deleteCatalog(long catalogId, LoginUser operator) { + CmsCatalog catalog = this.getById(catalogId); + long childCount = lambdaQuery().eq(CmsCatalog::getParentId, catalog.getCatalogId()).count(); + Assert.isTrue(childCount == 0, ContentCoreErrorCode.DEL_CHILD_FIRST::exception); + // 删除前事件发布 + applicationContext.publishEvent(new BeforeCatalogDeleteEvent(this, catalog, operator)); + // 删除栏目 + AsyncTaskManager.setTaskMessage("正在删除栏目数据"); + if (catalog.getParentId() > 0) { + CmsCatalog parentCatalog = getById(catalog.getParentId()); + parentCatalog.setChildCount(parentCatalog.getChildCount() - 1); + updateById(parentCatalog); + } + removeById(catalogId); + clearCache(catalog); + + applicationContext.publishEvent(new AfterCatalogDeleteEvent(this, catalog)); + return catalog; + } + + @Override + public String getCatalogLink(CmsCatalog catalog, int pageIndex, String publishPipeCode, boolean isPreview) { + CmsSite site = this.siteService.getSite(catalog.getSiteId()); + return CatalogUtils.getCatalogLink(site, catalog, pageIndex, publishPipeCode, isPreview); + } + + @Override + public String getCatalogListLink(CmsCatalog catalog, int pageIndex, String publishPipeCode, boolean isPreview) { + CmsSite site = this.siteService.getSite(catalog.getSiteId()); + return CatalogUtils.getCatalogListLink(site, catalog, pageIndex, publishPipeCode, isPreview); + } + + @Override + public void applyConfigPropsToChildren(CatalogApplyConfigPropsDTO dto) { + CmsCatalog catalog = this.getCatalog(dto.getCatalogId()); + + LambdaQueryWrapper q = new LambdaQueryWrapper<>(); + if (StringUtils.isEmpty(dto.getToCatalogIds())) { + q.likeRight(CmsCatalog::getAncestors, catalog.getAncestors() + CatalogUtils.ANCESTORS_SPLITER); + } else { + q.in(CmsCatalog::getCatalogId, dto.getToCatalogIds()); + } + List toCatalogs = this.list(q); + for (CmsCatalog toCatalog : toCatalogs) { + if (dto.isAllExtends()) { + toCatalog.setConfigProps(catalog.getConfigProps()); + } else if (StringUtils.isNotEmpty(dto.getConfigPropKeys())) { + dto.getConfigPropKeys().forEach(propKey -> { + toCatalog.getConfigProps().put(propKey, catalog.getConfigProps().get(propKey)); + }); + } + toCatalog.updateBy(dto.getOperator().getUsername()); + } + this.updateBatchById(toCatalogs); + toCatalogs.forEach(this::clearCache); + } + + @Override + public void applyPublishPipePropsToChildren(CatalogApplyPublishPipeDTO dto) { + CmsCatalog catalog = this.getCatalog(dto.getCatalogId()); + + LambdaQueryWrapper q = new LambdaQueryWrapper<>(); + q.likeRight(CmsCatalog::getAncestors, catalog.getAncestors() + CatalogUtils.ANCESTORS_SPLITER); + List toCatalogs = this.list(q); + for (CmsCatalog toCatalog : toCatalogs) { + Map publishPipeProps = toCatalog.getPublishPipeProps(dto.getPublishPipeCode()); + for (String propKey : dto.getPublishPipePropKeys()) { + publishPipeProps.put(propKey, catalog.getPublishPipeProps(dto.getPublishPipeCode()).get(propKey)); + } + toCatalog.updateBy(dto.getOperator().getUsername()); + } + this.updateBatchById(toCatalogs); + toCatalogs.forEach(this::clearCache); + } + + @Override + public void applySiteDefaultTemplateToCatalog(SiteDefaultTemplateDTO dto) { + CmsSite site = this.siteService.getSite(dto.getSiteId()); + + LambdaQueryWrapper q = new LambdaQueryWrapper().in(CmsCatalog::getCatalogId, + dto.getToCatalogIds()); + List toCatalogs = this.list(q); + if (!toCatalogs.isEmpty()) { + for (CmsCatalog toCatalog : toCatalogs) { + List publishPipeProps = dto.getPublishPipeProps(); + for (PublishPipeProp publishPipeProp : publishPipeProps) { + Map sitePublishPipeProp = site.getPublishPipeProps() + .get(publishPipeProp.getPipeCode()); + Map catalogPublishPipeProp = toCatalog + .getPublishPipeProps(publishPipeProp.getPipeCode()); + publishPipeProp.getProps().keySet().forEach(key -> { + catalogPublishPipeProp.put(key, sitePublishPipeProp.get(key)); + }); + } + toCatalog.updateBy(dto.getOperator().getUsername()); + } + this.updateBatchById(toCatalogs); + } + } + + @Override + public void clearCache(CmsCatalog catalog) { + this.redisCache.deleteObject(CACHE_PREFIX_ID + catalog.getCatalogId()); + this.redisCache.deleteObject(CACHE_PREFIX_ALIAS + catalog.getSiteId() + ":" + catalog.getAlias()); + } + + private void setCatalogCache(CmsCatalog catalog) { + this.redisCache.setCacheObject(CACHE_PREFIX_ID + catalog.getCatalogId(), catalog); + this.redisCache.setCacheObject(CACHE_PREFIX_ALIAS + catalog.getSiteId() + ":" + catalog.getAlias(), catalog); + } + + @Override + public void changeVisible(Long catalogId, String visible) { + CmsCatalog catalog = this.getCatalog(catalogId); + if (StringUtils.equals(visible, catalog.getVisibleFlag())) { + return; + } + catalog.setVisibleFlag(YesOrNo.isYes(visible) ? YesOrNo.YES : YesOrNo.NO); + this.updateById(catalog); + this.clearCache(catalog); + } + + @Override + public AsyncTask moveCatalog(CmsCatalog fromCatalog, CmsCatalog toCatalog) { + // 所有需要迁移的子栏目,按Ancestors排序依次处理 + List children = this.lambdaQuery().ne(CmsCatalog::getCatalogId, fromCatalog.getCatalogId()) + .likeRight(CmsCatalog::getAncestors, fromCatalog.getAncestors()).orderByAsc(CmsCatalog::getAncestors) + .list(); + // 判断栏目ancestors长度是否会超过限制 + int baseTreeLevel = Objects.isNull(toCatalog) ? 1 : toCatalog.getTreeLevel() + 1; + int maxTreelevel = baseTreeLevel; + for (CmsCatalog catalog : children) { + maxTreelevel = Math.max(maxTreelevel, catalog.getTreeLevel() - fromCatalog.getTreeLevel() + baseTreeLevel); + } + Assert.isTrue(ContentCoreConsts.CATALOG_MAX_TREE_LEVEL >= maxTreelevel, + ContentCoreErrorCode.CATALOG_MAX_TREE_LEVEL::exception); + AsyncTask task = new AsyncTask() { + + @Override + public void run0() { + moveCatalog0(fromCatalog, toCatalog, children); + } + }; + // 设置唯一任务ID避免同步执行,可能会导致数据错乱。 + task.setTaskId("CatalogMove"); + this.asyncTaskManager.execute(task); + return task; + } + + @Transactional(rollbackFor = Throwable.class) + private void moveCatalog0(CmsCatalog fromCatalog, CmsCatalog toCatalog, List children) { + Map invokedCatalogs = new HashMap<>(); + AsyncTaskManager.setTaskPercent(10); + // 1、原父级栏目子节点数-1 + if (fromCatalog.getParentId() > 0) { + AsyncTaskManager.setTaskMessage("更新转移栏目原父级栏目数据"); + CmsCatalog parent = this.getById(fromCatalog.getParentId()); + parent.setChildCount(parent.getChildCount() - 1); + invokedCatalogs.put(parent.getCatalogId(), parent); + } + // 2、来源栏目修改相关属性 + AsyncTaskManager.setTaskMessage("更新转移栏目数据"); + fromCatalog.setParentId(Objects.isNull(toCatalog) ? 0 : toCatalog.getCatalogId()); + String fromCatalogAncestors = CatalogUtils.getCatalogAncestors(toCatalog, fromCatalog.getCatalogId()); + fromCatalog.setAncestors(fromCatalogAncestors); + fromCatalog.setTreeLevel(Objects.isNull(toCatalog) ? 1 : toCatalog.getTreeLevel() + 1); + fromCatalog.setSortFlag(SortUtils.getDefaultSortValue()); + invokedCatalogs.put(fromCatalog.getCatalogId(), fromCatalog); + // 3、依次处理所有栏目 + for (CmsCatalog child : children) { + AsyncTaskManager.setTaskMessage("更新转移栏目子栏目数据"); + CmsCatalog parent = invokedCatalogs.get(child.getParentId()); + child.setAncestors(CatalogUtils.getCatalogAncestors(parent.getAncestors(), child.getCatalogId())); + child.setTreeLevel(parent.getTreeLevel() + 1); + invokedCatalogs.put(child.getCatalogId(), child); + } + // 4、目标栏目子栏目数+1 + if (Objects.nonNull(toCatalog)) { + AsyncTaskManager.setTaskMessage("更新转移目标栏目数据"); + toCatalog.setChildCount(toCatalog.getChildCount() + 1); + invokedCatalogs.put(toCatalog.getCatalogId(), toCatalog); + } + AsyncTaskManager.setTaskPercent(20); + // 5、批量更新数据库 + invokedCatalogs.values().forEach(catalog -> { + this.lambdaUpdate().set(CmsCatalog::getParentId, catalog.getParentId()) + .set(CmsCatalog::getAncestors, catalog.getAncestors()) + .set(CmsCatalog::getTreeLevel, catalog.getTreeLevel()) + .set(CmsCatalog::getChildCount, catalog.getChildCount()) + .eq(CmsCatalog::getCatalogId, catalog.getCatalogId()).update(); + }); + AsyncTaskManager.setTaskPercent(30); + // 6、更新缓存 + invokedCatalogs.values().forEach(this::clearCache); + // 7、更新除目标栏目外的所有栏目内容数据 + AsyncTaskManager.setTaskProgressInfo(40, "更新转移栏目及其子栏目内容"); + invokedCatalogs.values().forEach(catalog -> { + if (toCatalog == null || !Objects.equals(catalog.getCatalogId(), toCatalog.getCatalogId())) { + new LambdaUpdateChainWrapper<>(contentMapper) + .set(CmsContent::getCatalogAncestors, catalog.getAncestors()) + .eq(CmsContent::getCatalogId, catalog.getCatalogId()).update(); + } + }); + // 8、其他扩展,例如:重建栏目内容索引 + this.applicationContext.publishEvent(new AfterCatalogMoveEvent(this, fromCatalog, toCatalog, children)); + } + + @Override + public void setStaticPath(CmsCatalog catalog, TemplateContext context, boolean hasIndex) { + CmsSite site = this.siteService.getSite(catalog.getSiteId()); + String siteRoot = SiteUtils.getSiteRoot(site, context.getPublishPipeCode()); + context.setDirectory(siteRoot + catalog.getPath()); + String suffix = site.getStaticSuffix(context.getPublishPipeCode()); + String name = hasIndex ? "list" : "index"; + context.setFirstFileName(name + StringUtils.DOT + suffix); + context.setOtherFileName(name + "_" + TemplateContext.PlaceHolder_PageNo + StringUtils.DOT + suffix); + } + + @Override + public void saveCatalogExtends(Long catalogId, Map configs, String operator) { + CmsCatalog catalog = this.getCatalog(catalogId); + ConfigPropertyUtils.filterConfigProps(configs, catalog.getConfigProps(), IProperty.UseType.Catalog); + + catalog.setConfigProps(configs); + catalog.updateBy(operator); + this.updateById(catalog); + this.clearCache(catalog); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void sortCatalog(Long catalogId, Integer sort) { + CmsCatalog catalog = this.getCatalog(catalogId); + if (sort < 0) { + // 上移 + List beforeCatalogs = this.lambdaQuery() + .select(List.of(CmsCatalog::getSiteId, CmsCatalog::getCatalogId, CmsCatalog::getAlias, + CmsCatalog::getSortFlag)) + .eq(CmsCatalog::getSiteId, catalog.getSiteId()).eq(CmsCatalog::getParentId, catalog.getParentId()) + .lt(CmsCatalog::getSortFlag, catalog.getSortFlag()).orderByDesc(CmsCatalog::getSortFlag) + .page(new Page<>(1, Math.abs(sort), false)).getRecords(); + if (beforeCatalogs.isEmpty()) { + return; // 无需排序 + } + CmsCatalog targetCatalog = beforeCatalogs.get(beforeCatalogs.size() - 1); + // 更新排序值 + this.catalogSortPlusOne(targetCatalog.getSortFlag(), catalog.getSortFlag()); + this.lambdaUpdate().set(CmsCatalog::getSortFlag, targetCatalog.getSortFlag()) + .eq(CmsCatalog::getCatalogId, catalog.getCatalogId()).update(); + beforeCatalogs.forEach(this::clearCache); + } else { + // 下移 + List afterCatalogs = this.lambdaQuery() + .select(List.of(CmsCatalog::getSiteId, CmsCatalog::getCatalogId, CmsCatalog::getAlias, + CmsCatalog::getSortFlag)) + .eq(CmsCatalog::getSiteId, catalog.getSiteId()).eq(CmsCatalog::getParentId, catalog.getParentId()) + .gt(CmsCatalog::getSortFlag, catalog.getSortFlag()).orderByAsc(CmsCatalog::getSortFlag) + .page(new Page<>(1, sort, false)).getRecords(); + if (afterCatalogs.isEmpty()) { + return; // 无需排序 + } + CmsCatalog targetCatalog = afterCatalogs.get(afterCatalogs.size() - 1); + // 更新排序值 + this.catalogSortMinusOne(catalog.getSortFlag(), targetCatalog.getSortFlag()); + this.lambdaUpdate().set(CmsCatalog::getSortFlag, targetCatalog.getSortFlag()) + .eq(CmsCatalog::getCatalogId, catalog.getCatalogId()).update(); + afterCatalogs.forEach(this::clearCache); + } + this.clearCache(catalog); + } + + /** + * 排序标识范围内[startSort, endSort)的所有栏目排序值+1 + */ + private void catalogSortPlusOne(long startSort, long endSort) { + List catalogs = this.lambdaQuery() + .select(CmsCatalog::getCatalogId, CmsCatalog::getSortFlag) + .ge(CmsCatalog::getSortFlag, startSort) + .lt(CmsCatalog::getSortFlag, endSort) + .list(); + catalogs.forEach(catalog -> { + this.lambdaUpdate().set(CmsCatalog::getSortFlag, catalog.getSortFlag() + 1) + .eq(CmsCatalog::getCatalogId, catalog.getCatalogId()).update(); + }); + } + + /** + * 排序标识范围内(startSort, endSort]的所有栏目排序值-1 + */ + private void catalogSortMinusOne(long startSort, long endSort) { + List catalogs = this.lambdaQuery() + .select(CmsCatalog::getCatalogId, CmsCatalog::getSortFlag) + .gt(CmsCatalog::getSortFlag, startSort) + .le(CmsCatalog::getSortFlag, endSort) + .list(); + catalogs.forEach(catalog -> { + this.lambdaUpdate().set(CmsCatalog::getSortFlag, catalog.getSortFlag() - 1) + .eq(CmsCatalog::getCatalogId, catalog.getCatalogId()).update(); + }); + } + + @Override + public void changeContentCount(Long catalogId, int delta) { + RLock lock = redissonClient.getLock("Catalog-" + catalogId); + lock.lock(); + try { + CmsCatalog catalog = getById(catalogId); + lambdaUpdate().set(CmsCatalog::getContentCount, catalog.getContentCount() + delta) + .eq(CmsCatalog::getCatalogId, catalog.getCatalogId()).update(); + } finally { + lock.unlock(); + } + } +} diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/ResourceServiceImpl.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/ResourceServiceImpl.java index 2c56049e..785af05c 100644 --- a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/ResourceServiceImpl.java +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/service/impl/ResourceServiceImpl.java @@ -87,7 +87,7 @@ public class ResourceServiceImpl extends ServiceImpl CommonErrorCode.REQUEST_FAILED.exception(url)); CmsResource resource = new CmsResource(); diff --git a/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/user/preference/CatalogTreeExpandModePreference.java b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/user/preference/CatalogTreeExpandModePreference.java new file mode 100644 index 00000000..d7e16020 --- /dev/null +++ b/chestnut-cms/chestnut-cms-contentcore/src/main/java/com/chestnut/contentcore/user/preference/CatalogTreeExpandModePreference.java @@ -0,0 +1,52 @@ +/* + * 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.user.preference; + +import com.chestnut.common.security.domain.LoginUser; +import com.chestnut.common.utils.StringUtils; +import com.chestnut.system.domain.SysUser; +import com.chestnut.system.user.preference.IUserPreference; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.MapUtils; +import org.springframework.stereotype.Component; + +/** + * 栏目树展开方式 + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Component +@RequiredArgsConstructor +public class CatalogTreeExpandModePreference implements IUserPreference { + + public static final String ID = "CatalogTreeExpandMode"; + + @Override + public String getId() { + return ID; + } + + @Override + public String getName() { + return "栏目树展开模式"; + } + + public static String getValue(LoginUser loginUser) { + SysUser user = (SysUser) loginUser.getUser(); + return MapUtils.getString(user.getPreferences(), ID, StringUtils.EMPTY); + } +} diff --git a/chestnut-cms/chestnut-cms-seo/src/main/java/com/chestnut/seo/service/BaiduPushService.java b/chestnut-cms/chestnut-cms-seo/src/main/java/com/chestnut/seo/service/BaiduPushService.java index 866842e7..13d71398 100644 --- a/chestnut-cms/chestnut-cms-seo/src/main/java/com/chestnut/seo/service/BaiduPushService.java +++ b/chestnut-cms/chestnut-cms-seo/src/main/java/com/chestnut/seo/service/BaiduPushService.java @@ -29,6 +29,7 @@ import com.chestnut.seo.properties.BaiduPushAccessSecretProperty; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.net.URI; @@ -42,6 +43,7 @@ import java.util.Map; * @author 兮玥 * @email 190785909@qq.com */ +@Slf4j @Service @RequiredArgsConstructor public class BaiduPushService { @@ -72,10 +74,14 @@ public class BaiduPushService { 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); + try { + 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); + } catch (Exception e) { + log.error("Publish content to baidu failed!", e); + } }); return results; } diff --git a/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/domain/TreeExpandMode.java b/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/domain/TreeExpandMode.java new file mode 100644 index 00000000..02d1d32e --- /dev/null +++ b/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/domain/TreeExpandMode.java @@ -0,0 +1,35 @@ +/* + * 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.common.domain; + +/** + * 树展开模式 + * + * @author 兮玥 + * @email 190785909@qq.com + */ +public enum TreeExpandMode { + + /** + * 普通模式 + */ + Normal, + + /** + * 手风琴模式 + */ + Accordion +} diff --git a/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/domain/TreeNode.java b/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/domain/TreeNode.java index 5c9e22cf..7253a81e 100644 --- a/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/domain/TreeNode.java +++ b/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/domain/TreeNode.java @@ -93,6 +93,11 @@ public class TreeNode implements Serializable { return result; } + public static TreeVO build(List> list, String rootName) { + List> treeNodes = build(list); + return TreeVO.newInstance(rootName, treeNodes); + } + public TreeNode(T nodeId, T parentId, String nodeName, boolean isRoot) { this.id = nodeId; this.parentId = parentId; diff --git a/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/domain/TreeVO.java b/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/domain/TreeVO.java new file mode 100644 index 00000000..8fb6eb5c --- /dev/null +++ b/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/domain/TreeVO.java @@ -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.common.domain; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +/** + * TreeVO + * + * @author 兮玥 + * @email 190785909@qq.com + */ +@Getter +@Setter +@NoArgsConstructor +public class TreeVO { + + public static final TreeVO EMPTY = TreeVO.newInstance("", List.of()); + + private String rootName; + + private List> treeNodes; + + private TreeExpandMode expandMode = TreeExpandMode.Normal; + + public TreeVO(String rootName, List> treeNodes) { + this.rootName = rootName; + this.treeNodes = treeNodes; + } + + public TreeVO(String rootName, List> treeNodes, TreeExpandMode expandMode) { + this.rootName = rootName; + this.treeNodes = treeNodes; + this.expandMode = expandMode; + } + + public static TreeVO newInstance(String rootName, List> treeNodes) { + return new TreeVO(rootName, treeNodes); + } + + public static TreeVO newInstance(String rootName, List> treeNodes, TreeExpandMode expandMode) { + return new TreeVO(rootName, treeNodes, expandMode); + } +} diff --git a/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/utils/HttpUtils.java b/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/utils/HttpUtils.java index 9dec9011..62457d36 100644 --- a/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/utils/HttpUtils.java +++ b/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/utils/HttpUtils.java @@ -17,6 +17,9 @@ package com.chestnut.common.utils; import org.apache.commons.lang3.RandomUtils; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; import java.io.IOException; import java.io.InputStream; import java.net.URI; @@ -27,6 +30,10 @@ import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse.BodyHandlers; import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; import java.time.Duration; import java.util.Map; import java.util.Objects; @@ -44,6 +51,40 @@ public class HttpUtils { return USER_AGENTS[index]; } + private static SSLContext trustAllSSLContext() throws NoSuchAlgorithmException, KeyManagementException { + TrustManager[] trustAllCerts = new TrustManager[] { + new X509TrustManager() { + public void checkClientTrusted(X509Certificate[] certs, String authType) {} + public void checkServerTrusted(X509Certificate[] certs, String authType) {} + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + } + }; + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustAllCerts, new SecureRandom()); + return sslContext; + } + + private static HttpClient buildHttpClient() throws NoSuchAlgorithmException, KeyManagementException { + return buildHttpClient(false); + } + + private static HttpClient buildHttpClient(boolean ignoreSSL) throws NoSuchAlgorithmException, KeyManagementException { + return buildHttpClient(ignoreSSL, Duration.ofSeconds(30)); + } + + private static HttpClient buildHttpClient(boolean ignoreSSL, Duration connectTimeout) throws NoSuchAlgorithmException, KeyManagementException { + HttpClient.Builder builder = HttpClient.newBuilder() + .connectTimeout(Objects.requireNonNullElse(connectTimeout, Duration.ofSeconds(30))) + .followRedirects(Redirect.ALWAYS); + if (ignoreSSL) { + builder.sslContext(trustAllSSLContext()); + } + return builder.build(); + } + /** * 发起一个简单的GET请求,同步返回字符串结果 *

@@ -69,26 +110,19 @@ public class HttpUtils { } } - public static String post(URI uri, String body, Map headers) { - try { - if (Objects.isNull(body)) { - body = StringUtils.EMPTY; - } - HttpClient httpClient = HttpClient.newBuilder() - .connectTimeout(Duration.ofSeconds(30)) - .followRedirects(Redirect.ALWAYS) - .build(); - HttpRequest.Builder builder = HttpRequest.newBuilder(uri) - .POST(BodyPublishers.ofString(body, StandardCharsets.UTF_8)); - headers.forEach(builder::header); - if (!headers.containsKey("Content-Type")) { - builder.header("Content-Type", "application/json"); - } - HttpRequest httpRequest = builder.build(); - return httpClient.send(httpRequest, BodyHandlers.ofString()).body(); - } catch (IOException | InterruptedException e) { - throw new RuntimeException(e); + public static String post(URI uri, String body, Map headers) throws Exception { + if (Objects.isNull(body)) { + body = StringUtils.EMPTY; } + HttpClient httpClient = buildHttpClient(true); + HttpRequest.Builder builder = HttpRequest.newBuilder(uri) + .POST(BodyPublishers.ofString(body, StandardCharsets.UTF_8)); + headers.forEach(builder::header); + if (!headers.containsKey("Content-Type")) { + builder.header("Content-Type", "application/json"); + } + HttpRequest httpRequest = builder.build(); + return httpClient.send(httpRequest, BodyHandlers.ofString()).body(); } /** @@ -102,79 +136,33 @@ public class HttpUtils { * @param jsonBody * @return */ - public static String postJSON(URI uri, String jsonBody){ + public static String postJSON(URI uri, String jsonBody) throws Exception { return post(uri, jsonBody, Map.of("User-Agent", USER_AGENTS[0])); } - public static byte[] syncDownload(String uri) { - HttpClient httpClient = HttpClient.newBuilder() - .connectTimeout(Duration.ofSeconds(30)) - .followRedirects(Redirect.ALWAYS) - .build(); + + public static byte[] syncDownload(String uri) throws Exception { + return syncDownload(uri, false); + } + + public static byte[] syncDownload(String uri, boolean ignoreSSL) throws Exception { + HttpClient httpClient = buildHttpClient(ignoreSSL); HttpRequest httpRequest = HttpRequest.newBuilder(URI.create(uri)) .setHeader("User-Agent", randomUserAgent()) .GET().build(); - try { - return httpClient.send(httpRequest, BodyHandlers.ofByteArray()).body(); - } catch (IOException | InterruptedException e) { - throw new RuntimeException(e); - } + return httpClient.send(httpRequest, BodyHandlers.ofByteArray()).body(); + } + + public static InputStream syncDownloadInputStream(String uri) throws Exception { + return syncDownloadInputStream(uri, true); } - public static InputStream syncDownloadInputStream(String uri) { - HttpClient httpClient = HttpClient.newBuilder() - .connectTimeout(Duration.ofSeconds(30)) - .followRedirects(Redirect.ALWAYS) - .build(); + public static InputStream syncDownloadInputStream(String uri, boolean ignoreSSL) throws Exception { + HttpClient httpClient = buildHttpClient(ignoreSSL); HttpRequest httpRequest = HttpRequest.newBuilder(URI.create(uri)) .setHeader("User-Agent", randomUserAgent()) .GET().build(); - try { - return httpClient.send(httpRequest, BodyHandlers.ofInputStream()).body(); - } catch (IOException | InterruptedException e) { - throw new RuntimeException(e); - } - } - - public static void main(String[] args) { - syncDownload("http://www.liandu24.com/wp-content/uploads/2023/07/2023070513161507784652.png"); - } - - /** - * 通过丢弃响应体的get请求获取响应头信息,抛弃get响应body - * - * @param uri - * @param headerName - * @return - */ - public static String getDiscardingContentType(String uri) { - return getDiscardingHeader(uri, "content-type"); - } - - /** - * 通过丢弃响应体的get请求获取响应头信息,抛弃get响应body - * - * @param uri - * @param headerName - * @return - */ - public static String getDiscardingHeader(String uri, String headerName) { - try { - HttpClient httpClient = HttpClient.newBuilder() - .connectTimeout(Duration.ofSeconds(30)) - .followRedirects(Redirect.ALWAYS) - .build(); - HttpRequest httpRequest = HttpRequest.newBuilder(URI.create(uri)) - .setHeader("User-Agent", randomUserAgent()) - .GET().build(); - Optional headerValue = httpClient.send(httpRequest, BodyHandlers.discarding()).headers().firstValue(headerName); - if (headerValue.isPresent()) { - return headerValue.get(); - } - } catch (IOException | InterruptedException e) { - throw new RuntimeException(e); - } - return null; + return httpClient.send(httpRequest, BodyHandlers.ofInputStream()).body(); } /** @@ -183,18 +171,39 @@ public class HttpUtils { * @param uri * @param destPath */ - public static void asyncDownload(String uri, Path destPath) { - HttpClient httpClient = HttpClient.newBuilder() - .connectTimeout(Duration.ofSeconds(30)) - .followRedirects(Redirect.ALWAYS) - .build(); + public static void asyncDownload(String uri, Path destPath) throws Exception { + HttpClient httpClient = buildHttpClient(true); HttpRequest httpRequest = HttpRequest.newBuilder(URI.create(uri)) .setHeader("User-Agent", randomUserAgent()) .GET().build(); httpClient.sendAsync(httpRequest, BodyHandlers.ofFile(destPath)); } - public static void asyncDownload(String uri, String destPath) { + public static void asyncDownload(String uri, String destPath) throws Exception { asyncDownload(uri, Path.of(destPath)); } + + /** + * 通过丢弃响应体的get请求获取响应头信息,抛弃get响应body + * + * @param uri + */ + public static String getDiscardingContentType(String uri) throws Exception { + return getDiscardingHeader(uri, "content-type"); + } + + /** + * 通过丢弃响应体的get请求获取响应头信息,抛弃get响应body + * + * @param uri + * @param headerName + */ + public static String getDiscardingHeader(String uri, String headerName) throws Exception { + HttpClient httpClient = buildHttpClient(true); + HttpRequest httpRequest = HttpRequest.newBuilder(URI.create(uri)) + .setHeader("User-Agent", randomUserAgent()) + .GET().build(); + Optional headerValue = httpClient.send(httpRequest, BodyHandlers.discarding()).headers().firstValue(headerName); + return headerValue.orElse(null); + } } diff --git a/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/utils/file/FileExUtils.java b/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/utils/file/FileExUtils.java index 19bbe7a4..0db355b2 100644 --- a/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/utils/file/FileExUtils.java +++ b/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/utils/file/FileExUtils.java @@ -150,7 +150,7 @@ public class FileExUtils { * @param url 图片地址 * @return 图片后缀 */ - public static String getImageSuffix(String url) { + public static String getImageSuffix(String url) throws Exception { String extension = getExtension(url); if (StringUtils.isEmpty(extension)) { // 尝试从response.header.content-type获取 diff --git a/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/validation/AddGroup.java b/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/validation/AddGroup.java new file mode 100644 index 00000000..c507f47b --- /dev/null +++ b/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/validation/AddGroup.java @@ -0,0 +1,24 @@ +/* + * 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.common.validation; + +/** + * 校验分组:新建 + * + * @author 兮玥 + * @email 190785909@qq.com + */ +public interface AddGroup {} diff --git a/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/validation/EditGroup.java b/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/validation/EditGroup.java new file mode 100644 index 00000000..1172ba96 --- /dev/null +++ b/chestnut-common/chestnut-common-core/src/main/java/com/chestnut/common/validation/EditGroup.java @@ -0,0 +1,24 @@ +/* + * 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.common.validation; + +/** + * 校验分组:编辑 + * + * @author 兮玥 + * @email 190785909@qq.com + */ +public interface EditGroup { } diff --git a/chestnut-common/chestnut-common-security/src/main/java/com/chestnut/common/security/exception/handler/GlobalExceptionHandler.java b/chestnut-common/chestnut-common-security/src/main/java/com/chestnut/common/security/exception/handler/GlobalExceptionHandler.java index 0af7bc9b..e326da43 100644 --- a/chestnut-common/chestnut-common-security/src/main/java/com/chestnut/common/security/exception/handler/GlobalExceptionHandler.java +++ b/chestnut-common/chestnut-common-security/src/main/java/com/chestnut/common/security/exception/handler/GlobalExceptionHandler.java @@ -27,12 +27,15 @@ import lombok.RequiredArgsConstructor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.context.support.DefaultMessageSourceResolvable; import org.springframework.http.HttpStatus; import org.springframework.validation.BindException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; +import java.util.stream.Collectors; + /** * 全局异常处理器 * @@ -112,6 +115,9 @@ public class GlobalExceptionHandler { */ @ExceptionHandler(BindException.class) public R handleBindException(BindException e) { - return R.fail(e.getMessage()); + String errMsg = e.getBindingResult().getAllErrors().stream() + .map(DefaultMessageSourceResolvable::getDefaultMessage) + .collect(Collectors.joining("\n")); + return R.fail(errMsg); } } diff --git a/chestnut-modules/chestnut-system/src/main/java/com/chestnut/system/service/impl/SysMenuServiceImpl.java b/chestnut-modules/chestnut-system/src/main/java/com/chestnut/system/service/impl/SysMenuServiceImpl.java index c8ce0ba6..b3c754f6 100644 --- a/chestnut-modules/chestnut-system/src/main/java/com/chestnut/system/service/impl/SysMenuServiceImpl.java +++ b/chestnut-modules/chestnut-system/src/main/java/com/chestnut/system/service/impl/SysMenuServiceImpl.java @@ -41,10 +41,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; /** @@ -67,7 +64,7 @@ public class SysMenuServiceImpl extends ServiceImpl impl */ @Override public List buildRouters(List menus) { - List routers = new LinkedList(); + List routers = new LinkedList<>(); for (SysMenu menu : menus) { RouterVO router = new RouterVO(); router.setHidden(YesOrNo.isNo(menu.getVisible())); @@ -75,34 +72,34 @@ public class SysMenuServiceImpl extends ServiceImpl impl router.setPath(getRouterPath(menu)); router.setComponent(getComponent(menu)); router.setQuery(menu.getQuery()); - router.setMeta(new MetaVO(menu.getMenuName(), menu.getIcon(), YesOrNo.isYes(menu.getIsCache()), + router.setMeta(new MetaVO(menu.getMenuName(), menu.getIcon(), YesOrNo.isNo(menu.getIsCache()), menu.getPath())); List cMenus = menu.getChildren(); - if (!cMenus.isEmpty() && cMenus.size() > 0 && MenuType.isDirectory(menu.getMenuType())) { + if (!cMenus.isEmpty() && MenuType.isDirectory(menu.getMenuType())) { router.setAlwaysShow(true); router.setRedirect("noRedirect"); router.setChildren(buildRouters(cMenus)); } else if (isMenuFrame(menu)) { router.setMeta(null); - List childrenList = new ArrayList(); + List childrenList = new ArrayList<>(); RouterVO children = new RouterVO(); children.setPath(menu.getPath()); children.setComponent(menu.getComponent()); - children.setName(StringUtils.capitalize(menu.getPath())); + children.setName(parseRouteName(menu)); children.setMeta(new MetaVO(menu.getMenuName(), menu.getIcon(), - YesOrNo.isYes(menu.getIsCache()), menu.getPath())); + YesOrNo.isNo(menu.getIsCache()), menu.getPath())); children.setQuery(menu.getQuery()); childrenList.add(children); router.setChildren(childrenList); } else if (menu.getParentId().intValue() == 0 && isInnerLink(menu)) { router.setMeta(new MetaVO(menu.getMenuName(), menu.getIcon())); router.setPath("/"); - List childrenList = new ArrayList(); + List childrenList = new ArrayList<>(); RouterVO children = new RouterVO(); String routerPath = innerLinkReplaceEach(menu.getPath()); children.setPath(routerPath); children.setComponent(MenuComponentType.InnerLink.name()); - children.setName(StringUtils.capitalize(routerPath)); + children.setName(parseRouteName(menu)); children.setMeta(new MetaVO(menu.getMenuName(), menu.getIcon(), menu.getPath())); childrenList.add(children); router.setChildren(childrenList); @@ -239,12 +236,15 @@ public class SysMenuServiceImpl extends ServiceImpl impl * @return 路由名称 */ public String getRouteName(SysMenu menu) { - String routerName = StringUtils.capitalize(menu.getPath()); - // 非外链并且是一级目录(类型为目录) - if (isMenuFrame(menu)) { - routerName = StringUtils.EMPTY; + if (StringUtils.isEmpty(menu.getComponent()) || isMenuFrame(menu)) { + return StringUtils.EMPTY; } - return routerName; + return parseRouteName(menu); + } + + private String parseRouteName(SysMenu menu) { + return Arrays.stream(menu.getComponent().split("/")) + .map(StringUtils::capitalize).collect(Collectors.joining("")); } /** @@ -330,9 +330,9 @@ public class SysMenuServiceImpl extends ServiceImpl impl */ @Override public List getChildPerms(List list, int parentId) { - List returnList = new ArrayList(); + List returnList = new ArrayList<>(); for (Iterator iterator = list.iterator(); iterator.hasNext();) { - SysMenu t = (SysMenu) iterator.next(); + SysMenu t = iterator.next(); // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点 if (t.getParentId() == parentId) { recursionFn(list, t); @@ -344,9 +344,6 @@ public class SysMenuServiceImpl extends ServiceImpl impl /** * 递归列表 - * - * @param list - * @param t */ private void recursionFn(List list, SysMenu t) { // 得到子节点列表 @@ -363,14 +360,12 @@ public class SysMenuServiceImpl extends ServiceImpl impl * 得到子节点列表 */ private List getChildList(List list, SysMenu t) { - List tlist = new ArrayList(); - Iterator it = list.iterator(); - while (it.hasNext()) { - SysMenu n = (SysMenu) it.next(); - if (n.getParentId().longValue() == t.getMenuId().longValue()) { - tlist.add(n); - } - } + List tlist = new ArrayList<>(); + for (SysMenu n : list) { + if (n.getParentId().longValue() == t.getMenuId().longValue()) { + tlist.add(n); + } + } return tlist; } @@ -378,13 +373,11 @@ public class SysMenuServiceImpl extends ServiceImpl impl * 判断是否有子节点 */ private boolean hasChild(List list, SysMenu t) { - return getChildList(list, t).size() > 0; + return !getChildList(list, t).isEmpty(); } /** * 内链域名特殊字符替换 - * - * @return */ public String innerLinkReplaceEach(String path) { return StringUtils.replaceEach(path, diff --git a/chestnut-modules/chestnut-system/src/main/java/com/chestnut/system/user/preference/IUserPreference.java b/chestnut-modules/chestnut-system/src/main/java/com/chestnut/system/user/preference/IUserPreference.java index b1c74e25..0267fa61 100644 --- a/chestnut-modules/chestnut-system/src/main/java/com/chestnut/system/user/preference/IUserPreference.java +++ b/chestnut-modules/chestnut-system/src/main/java/com/chestnut/system/user/preference/IUserPreference.java @@ -15,6 +15,8 @@ */ package com.chestnut.system.user.preference; +import com.chestnut.common.utils.StringUtils; + /** * 用户偏好 配置项 * @@ -25,29 +27,26 @@ public interface IUserPreference { /** * 唯一标识 - * - * @return */ - public String getId(); + String getId(); /** * 显示名称 - * - * @return */ - public String getName(); + String getName(); /** * 校验数据 - * - * @param config */ - public boolean validate(Object config); + default boolean validate(Object config) { + return true; + } + /** * 默认值 - * - * @return */ - public Object getDefaultValue(); + default Object getDefaultValue() { + return StringUtils.EMPTY; + } } diff --git a/chestnut-ui/src/i18n/lang/en.js b/chestnut-ui/src/i18n/lang/en.js index 4f892017..d85dba32 100644 --- a/chestnut-ui/src/i18n/lang/en.js +++ b/chestnut-ui/src/i18n/lang/en.js @@ -23,6 +23,7 @@ export default { SaveSuccess: "Save Success", EditSuccess: 'Edit Success', DeleteSuccess: 'Delete Success', + CopySuccess: 'Copy Success', Yes: 'Yes', No: 'No', Enable: "Enable", @@ -294,7 +295,10 @@ export default { StatIndex: "Statistics Default Menu", IncludeChildContent: "Include Children In Content List", OpenContentEditorW: "New Window For Content Editor", - ShowContentSubTitle: "Show Content Subtitle" + ShowContentSubTitle: "Show Content Subtitle", + CatalogTreeExpandMode: "Catalog Tree Expand Mode", + CatalogTreeExpandMode_Normal: "Common", + CatalogTreeExpandMode_Accordion: "Accordion", }, UserRole: { UserInfo: "User Information", @@ -1092,6 +1096,7 @@ export default { }, Placeholder: { Title: "Input content title", + ImportCSS: "Select publish pipe style", }, Title: "Title", SubTitle: "Subtitle", @@ -1790,5 +1795,32 @@ export default { Query: "Input name..." } } + }, + Flowable: { + Category: { + AddCategory: "Add Category", + CategoryNamePlaceholder: "Input category name...", + SortUp: "Move Up", + SortDown: "Move Down", + ParentCategory: "Parent", + Name: "Name", + TreeRootName: "Model Category", + }, + Model: { + Key: "Key", + Name: "Name", + Version: "Version", + Status: "Status", + Design: "Design", + Suspend: "Suspend", + Resume: "Resume", + Category: "Category", + AddModelTitle: "Add Model", + DesignTitle: "Design Process", + Placeholder: { + ModelKey: "Input model key", + ModelName: "Input model name" + } + } } }; diff --git a/chestnut-ui/src/i18n/lang/zh-CN.js b/chestnut-ui/src/i18n/lang/zh-CN.js index 27544437..a47f7a10 100644 --- a/chestnut-ui/src/i18n/lang/zh-CN.js +++ b/chestnut-ui/src/i18n/lang/zh-CN.js @@ -23,6 +23,7 @@ export default { SaveSuccess: "保存成功", EditSuccess: '修改成功', DeleteSuccess: '删除成功', + CopySuccess: '复制成功', Yes: '是', No: '否', Enable: "启用", @@ -294,7 +295,10 @@ export default { StatIndex: "统计分析默认菜单", IncludeChildContent: "内容列表是否显示子栏目内容", OpenContentEditorW: "内容编辑是否使用新窗口", - ShowContentSubTitle: "默认显示内容副标题" + ShowContentSubTitle: "默认显示内容副标题", + CatalogTreeExpandMode: "栏目树展开模式", + CatalogTreeExpandMode_Normal: "普通模式", + CatalogTreeExpandMode_Accordion: "手风琴模式", }, UserRole: { UserInfo: "用户信息", @@ -1092,6 +1096,7 @@ export default { }, Placeholder: { Title: "输入内容标题", + ImportCSS: "选择发布通道样式", }, Title: "标题", SubTitle: "副标题", @@ -1793,5 +1798,32 @@ export default { Query: "输入名称" } } + }, + Flowable: { + Category: { + AddCategory: "添加分类", + CategoryNamePlaceholder: "输入分类名称", + SortUp: "上移", + SortDown: "下移", + ParentCategory: "父级分类", + Name: "名称", + TreeRootName: "模型分类", + }, + Model: { + Key: "唯一标识", + Name: "名称", + Version: "版本", + Status: "状态", + Design: "设计", + Suspend: "挂起", + Resume: "恢复", + Category: "所属分类", + AddModelTitle: "新建模型", + DesignTitle: "流程设计", + Placeholder: { + ModelKey: "输入模型标识", + ModelName: "输入模型名称" + } + } } }; diff --git a/chestnut-ui/src/i18n/lang/zh-TW.js b/chestnut-ui/src/i18n/lang/zh-TW.js index aaaf3a97..bd8d018c 100644 --- a/chestnut-ui/src/i18n/lang/zh-TW.js +++ b/chestnut-ui/src/i18n/lang/zh-TW.js @@ -23,6 +23,7 @@ export default { SaveSuccess: "保存成功", EditSuccess: '修改成功', DeleteSuccess: '刪除成功', + CopySuccess: '複製成功', Yes: '是', No: '否', Enable: "啟用", @@ -294,7 +295,10 @@ export default { StatIndex: "統計分析預設菜單", IncludeChildContent: "內容列表是否顯示子欄目內容", OpenContentEditorW: "內容編輯是否使用新窗口", - ShowContentSubTitle: "預設顯示內容副標題" + ShowContentSubTitle: "預設顯示內容副標題", + CatalogTreeExpandMode: "欄目樹展開模式", + CatalogTreeExpandMode_Normal: "普通模式", + CatalogTreeExpandMode_Accordion: "手風琴模式", }, UserRole: { UserInfo: "用戶資訊", @@ -1092,6 +1096,7 @@ export default { }, Placeholder: { Title: "輸入內容標題", + ImportCSS: "選擇發佈通道樣式", }, Title: "標題", SubTitle: "副標題", @@ -1790,5 +1795,32 @@ export default { Query: "輸入名稱" } } + }, + Flowable: { + Category: { + AddCategory: "添加分類", + CategoryNamePlaceholder: "輸入分類名稱", + SortUp: "上移", + SortDown: "下移", + ParentCategory: "父級分類", + Name: "名稱", + TreeRootName: "模型分類", + }, + Model: { + Key: "唯一標識", + Name: "名稱", + Version: "版本", + Status: "狀態", + Design: "設計", + Suspend: "掛起", + Resume: "恢復", + Category: "所屬分類", + AddModelTitle: "新建模型", + DesignTitle: "設計流程", + Placeholder: { + ModelKey: "輸入模型唯一標識", + ModelName: "輸入模型名稱" + } + } } }; diff --git a/chestnut-ui/src/views/cms/ad/adSpace.vue b/chestnut-ui/src/views/cms/ad/adSpace.vue index 3a02c558..49180fec 100644 --- a/chestnut-ui/src/views/cms/ad/adSpace.vue +++ b/chestnut-ui/src/views/cms/ad/adSpace.vue @@ -147,7 +147,7 @@ import { listAdSpaces, addAdSpace, editAdSpace, deleteAdSpace, publishAdSpace } import CMSTemplateSelector from '@/views/cms/contentcore/templateSelector'; export default { - name: "CMSAdSpace", + name: "CmsAdAdSpace", components: { 'cms-template-selector': CMSTemplateSelector }, diff --git a/chestnut-ui/src/views/cms/contentcore/catalog.vue b/chestnut-ui/src/views/cms/contentcore/catalog.vue index bbd2c775..014e68b8 100644 --- a/chestnut-ui/src/views/cms/contentcore/catalog.vue +++ b/chestnut-ui/src/views/cms/contentcore/catalog.vue @@ -34,7 +34,7 @@ import CMSCatalogInfo from '@/views/cms/contentcore/catalogInfo'; import CMSCatalogExtend from '@/views/cms/contentcore/catalogExtend'; export default { - name: "CMSCatalog", + name: "CmsContentcoreCatalog", components: { 'cms-catalog-tree': CMSCatalogTree, 'cms-catalog-info': CMSCatalogInfo, diff --git a/chestnut-ui/src/views/cms/contentcore/catalogTree.vue b/chestnut-ui/src/views/cms/contentcore/catalogTree.vue index 5d207d43..d062f4bd 100644 --- a/chestnut-ui/src/views/cms/contentcore/catalogTree.vue +++ b/chestnut-ui/src/views/cms/contentcore/catalogTree.vue @@ -35,6 +35,7 @@ :expand-on-click-node="false" :default-expanded-keys="treeExpandedKeys" :filter-node-method="filterNode" + :accordion="expandMode=='accordion'" node-key="id" ref="tree" highlight-current @@ -178,13 +179,14 @@ export default { // 栏目类型 catalogTypeOptions: [], // 栏目树过滤:栏目名称 - filterCatalogName: undefined, + filterCatalogName: "", // 栏目树数据 - catalogOptions: undefined, + catalogOptions: [], // 站点名称 - siteName: undefined, + siteName: "", + expandMode: "", // 当前选中栏目ID - selectedCatalogId: undefined, + selectedCatalogId: "", treeExpandedKeys: [], defaultProps: { children: "children", @@ -244,6 +246,7 @@ export default { this.$cache.local.remove("LastSelectedCatalogId"); } this.siteName = response.data.siteName; + this.expandMode = response.data.expandMode; this.loading = false; this.$nextTick(() => { this.selectedCatalogId = this.$cache.local.get("LastSelectedCatalogId"); diff --git a/chestnut-ui/src/views/cms/contentcore/content.vue b/chestnut-ui/src/views/cms/contentcore/content.vue index 0745786c..5467ed04 100644 --- a/chestnut-ui/src/views/cms/contentcore/content.vue +++ b/chestnut-ui/src/views/cms/contentcore/content.vue @@ -31,7 +31,7 @@ import CMSPageWidget from '@/views/cms/contentcore/pageWidget'; import CMSContentRecycleList from '@/views/cms/contentcore/contentRecycleList'; export default { - name: "CMSContent", + name: "CmsContentcoreContent", components: { 'cms-catalog-tree': CMSCatalogTree, 'cms-content-list': CMSContentList, diff --git a/chestnut-ui/src/views/cms/contentcore/contentEditor.vue b/chestnut-ui/src/views/cms/contentcore/contentEditor.vue index a915115a..30c31e30 100644 --- a/chestnut-ui/src/views/cms/contentcore/contentEditor.vue +++ b/chestnut-ui/src/views/cms/contentcore/contentEditor.vue @@ -147,14 +147,13 @@ - - - {{ pp.pipeName }} - + + + diff --git a/chestnut-ui/src/views/cms/contentcore/file.vue b/chestnut-ui/src/views/cms/contentcore/file.vue index 0ffbf1af..4ca67d31 100644 --- a/chestnut-ui/src/views/cms/contentcore/file.vue +++ b/chestnut-ui/src/views/cms/contentcore/file.vue @@ -182,7 +182,7 @@ import { getDirectoryTreeData, getFileList, renameFile, addFile, deleteFile } fr import { getConfigKey } from "@/api/system/config"; export default { - name: "CMSFile", + name: "CmsContentcoreFile", data () { return { treeSideHeight: 600, diff --git a/chestnut-ui/src/views/cms/contentcore/publishPipe.vue b/chestnut-ui/src/views/cms/contentcore/publishPipe.vue index 4e6d24fb..d6a247a8 100644 --- a/chestnut-ui/src/views/cms/contentcore/publishPipe.vue +++ b/chestnut-ui/src/views/cms/contentcore/publishPipe.vue @@ -107,7 +107,7 @@ import { getPublishPipeList, getPublishPipeData, addPublishPipe, updatePublishPipe, delPublishPipe } from "@/api/contentcore/publishpipe"; export default { - name: "CMSPublishPipe", + name: "CmsContentcorePublishPipe", dicts: ['EnableOrDisable'], data () { return { diff --git a/chestnut-ui/src/views/cms/contentcore/resource.vue b/chestnut-ui/src/views/cms/contentcore/resource.vue index 85b38e81..1637bca1 100644 --- a/chestnut-ui/src/views/cms/contentcore/resource.vue +++ b/chestnut-ui/src/views/cms/contentcore/resource.vue @@ -171,7 +171,7 @@ import { getResourceTypes, getResrouceList, getResourceDetail, delResource } fro import { getConfigKey } from "@/api/system/config"; export default { - name: "CmsResource", + name: "CmsContentcoreResource", data () { return { // 遮罩层 diff --git a/chestnut-ui/src/views/cms/contentcore/site.vue b/chestnut-ui/src/views/cms/contentcore/site.vue index 450a5dad..ae20e0fb 100644 --- a/chestnut-ui/src/views/cms/contentcore/site.vue +++ b/chestnut-ui/src/views/cms/contentcore/site.vue @@ -132,7 +132,7 @@ import { delSite, addSite, listSite, publishSite } from "@/api/contentcore/site import CMSProgress from '@/views/components/Progress'; export default { - name: "Site", + name: "CmsContentcoreSite", components: { 'cms-progress': CMSProgress, }, diff --git a/chestnut-ui/src/views/cms/contentcore/template.vue b/chestnut-ui/src/views/cms/contentcore/template.vue index b57f6cac..6f03953b 100644 --- a/chestnut-ui/src/views/cms/contentcore/template.vue +++ b/chestnut-ui/src/views/cms/contentcore/template.vue @@ -191,7 +191,7 @@ import { getConfigKey } from "@/api/system/config" import { getTemplateList, getTemplateDetail, renameTemplate, addTemplate, delTemplate, clearIncludeCache } from "@/api/contentcore/template"; export default { - name: "CmsTemplate", + name: "CmsContentcoreTemplate", data () { const validatePath = (rule, value, callback) => { if (!value || value.length == 0 || !value.endsWith(this.templateSuffix)) { diff --git a/chestnut-ui/src/views/cms/customform/index.vue b/chestnut-ui/src/views/cms/customform/index.vue index 3c89f4d6..2eac1e88 100644 --- a/chestnut-ui/src/views/cms/customform/index.vue +++ b/chestnut-ui/src/views/cms/customform/index.vue @@ -224,7 +224,7 @@ import { listCustomForms, getCustomForm, addCustomForm, editCustomForm, deleteCu import CMSTemplateSelector from '@/views/cms/contentcore/templateSelector'; export default { - name: "CustomFormList", + name: "CmsCustomformIndex", dicts: [ 'CustomFormStatus', 'CustomFormRule' ], components: { 'cms-template-selector': CMSTemplateSelector, diff --git a/chestnut-ui/src/views/cms/exmodel/model.vue b/chestnut-ui/src/views/cms/exmodel/model.vue index 48f76e67..a7a3e9c9 100644 --- a/chestnut-ui/src/views/cms/exmodel/model.vue +++ b/chestnut-ui/src/views/cms/exmodel/model.vue @@ -123,7 +123,7 @@ import { listModelDataTables } from "@/api/meta/model"; import { addXModel, editXModel, deleteXModel, listXModel } from "@/api/contentcore/exmodel"; export default { - name: "CMSEXmodel", + name: "CmsExmodelModel", data () { return { // 遮罩层 diff --git a/chestnut-ui/src/views/cms/link/linkGroup.vue b/chestnut-ui/src/views/cms/link/linkGroup.vue index 92e2818b..87c116a8 100644 --- a/chestnut-ui/src/views/cms/link/linkGroup.vue +++ b/chestnut-ui/src/views/cms/link/linkGroup.vue @@ -134,7 +134,7 @@ import { codeValidator } from '@/utils/validate' import { getLinkGroupList, addLinkGroup, editLinkGroup, deleteLinkGroup } from "@/api/link/linkGroup"; export default { - name: "CmsLinkGroup", + name: "CmsLinkLinkGroup", data () { return { // 遮罩层 diff --git a/chestnut-ui/src/views/cms/search/indexList.vue b/chestnut-ui/src/views/cms/search/indexList.vue index f300417e..3ea79349 100644 --- a/chestnut-ui/src/views/cms/search/indexList.vue +++ b/chestnut-ui/src/views/cms/search/indexList.vue @@ -140,7 +140,7 @@ import { getContentTypes } from "@/api/contentcore/catalog"; import { getContentIndexList, deleteContentIndex, rebuildIndex } from "@/api/contentcore/search"; export default { - name: "CMSIndexList", + name: "CmsSearchIndexList", dicts: ['CMSContentStatus', 'CMSContentAttribute'], components: { 'cms-progress': CMSProgress diff --git a/chestnut-ui/src/views/cms/staticize/index.vue b/chestnut-ui/src/views/cms/staticize/index.vue index 1eabdb15..ee7dd80b 100644 --- a/chestnut-ui/src/views/cms/staticize/index.vue +++ b/chestnut-ui/src/views/cms/staticize/index.vue @@ -23,7 +23,7 @@ import CMSDynamicTemplate from '@/views/cms/staticize/dynamicList'; import CMSCustomDynamicTemplate from '@/views/cms/staticize/customDynamicList'; export default { - name: "CMSStaticize", + name: "CmsStaticizeIndex", components: { 'cms-template-tag': CMSTemplateTag, 'cms-template-function': CMSTemplateFunc, diff --git a/chestnut-ui/src/views/cms/word/word.vue b/chestnut-ui/src/views/cms/word/word.vue index a8097d77..efe2db8e 100644 --- a/chestnut-ui/src/views/cms/word/word.vue +++ b/chestnut-ui/src/views/cms/word/word.vue @@ -25,7 +25,7 @@ import CMSSensitiveWord from '@/views/word/sensitiveWord'; import CMSErrorProneWord from '@/views/word/errorProneWord'; export default { - name: "CMSWordTab", + name: "CmsWordWord", components: { 'cms-tag-word': CMSTagWord, 'cms-hot-word': CMSHotWord, diff --git a/chestnut-ui/src/views/comment/commentList.vue b/chestnut-ui/src/views/comment/commentList.vue index 41a906e6..714c28eb 100644 --- a/chestnut-ui/src/views/comment/commentList.vue +++ b/chestnut-ui/src/views/comment/commentList.vue @@ -212,7 +212,7 @@ import { getCommentList, getCommentReplyList, getCommentLikeList, deleteComments import CommentLikeDialog from '@/views/comment/commentLike'; export default { - name: "CommentList", + name: "CommentCommentList", components: { "comment-like-dialog": CommentLikeDialog }, diff --git a/chestnut-ui/src/views/components/icons/index.vue b/chestnut-ui/src/views/components/icons/index.vue index d3c9a719..150257f7 100644 --- a/chestnut-ui/src/views/components/icons/index.vue +++ b/chestnut-ui/src/views/components/icons/index.vue @@ -11,7 +11,7 @@

{{ generateIconCode(item) }}
-
+
{{ item }}
@@ -24,9 +24,9 @@
{{ generateElementIconCode(item) }}
-
- - {{ item }} +
+ + {{ item }}
@@ -40,7 +40,7 @@ import svgIcons from './svg-icons' import elementIcons from './element-icons' export default { - name: 'Icons', + name: 'ComponentsIconsIndex', data() { return { svgIcons, @@ -53,6 +53,9 @@ export default { }, generateElementIconCode(symbol) { return `` + }, + clipboardSuccess() { + this.$modal.msgSuccess(this.$t('Common.CopySuccess')); } } } diff --git a/chestnut-ui/src/views/member/expConfig.vue b/chestnut-ui/src/views/member/expConfig.vue index 3fc4f963..4de61933 100644 --- a/chestnut-ui/src/views/member/expConfig.vue +++ b/chestnut-ui/src/views/member/expConfig.vue @@ -221,7 +221,7 @@ import { getLevelTypes } from "@/api/member/levelConfig"; import { getExpOperations, getExpConfigList, getExpConfigDetail, addExpConfig, updateExpConfig, deleteExpConfigs } from "@/api/member/expConfig"; export default { - name: "MemberExpOperation", + name: "MemberExpConfig", data () { return { // 遮罩层 diff --git a/chestnut-ui/src/views/member/levelConfig.vue b/chestnut-ui/src/views/member/levelConfig.vue index aa0c9b32..90c6dbe4 100644 --- a/chestnut-ui/src/views/member/levelConfig.vue +++ b/chestnut-ui/src/views/member/levelConfig.vue @@ -158,7 +158,7 @@ import { getLevelTypes, getLevelConfigList, getLevelConfigDetail, addLevelConfig, updateLevelConfig, deleteLevelConfigs } from "@/api/member/levelConfig"; export default { - name: "MemberExpOperation", + name: "MemberLevelConfig", data () { return { // 遮罩层 diff --git a/chestnut-ui/src/views/member/memberList.vue b/chestnut-ui/src/views/member/memberList.vue index 25a19d56..ecd38bbc 100644 --- a/chestnut-ui/src/views/member/memberList.vue +++ b/chestnut-ui/src/views/member/memberList.vue @@ -236,7 +236,7 @@ import { isBlank, validEmail, validPhoneNumber } from '@/utils/validate'; import { getMemberList, getMemberDetail, addMember, updateMember, deleteMembers, resetMemberPassword } from "@/api/member/member"; export default { - name: "MemberList", + name: "MemberMemberList", dicts: [ 'MemberStatus' ], data () { const validateMember = (rule, value, callback) => { diff --git a/chestnut-ui/src/views/monitor/async/index.vue b/chestnut-ui/src/views/monitor/async/index.vue index cf4fbc4c..67138795 100644 --- a/chestnut-ui/src/views/monitor/async/index.vue +++ b/chestnut-ui/src/views/monitor/async/index.vue @@ -99,7 +99,7 @@ import { MessageBox } from 'element-ui' import { getTaskList, stopTask, removeTask } from "@/api/system/async"; export default { - name: "CmsAsyncTaskList", + name: "MonitorAsyncIndex", data () { return { // 遮罩层 diff --git a/chestnut-ui/src/views/monitor/cache/index.vue b/chestnut-ui/src/views/monitor/cache/index.vue index dccb35f5..9d79c640 100644 --- a/chestnut-ui/src/views/monitor/cache/index.vue +++ b/chestnut-ui/src/views/monitor/cache/index.vue @@ -71,7 +71,7 @@ import { getCache } from "@/api/monitor/cache"; import echarts from "echarts"; export default { - name: "Cache", + name: "MonitorCacheIndex", data() { return { // 统计命令信息 diff --git a/chestnut-ui/src/views/monitor/cache/list.vue b/chestnut-ui/src/views/monitor/cache/list.vue index 2a786a4a..1e92d3ba 100644 --- a/chestnut-ui/src/views/monitor/cache/list.vue +++ b/chestnut-ui/src/views/monitor/cache/list.vue @@ -160,7 +160,7 @@ import { listCacheName, listCacheKey, getCacheValue, clearCacheName, clearCacheKey, clearCacheAll } from "@/api/monitor/cache"; export default { - name: "CacheList", + name: "MonitorCacheList", data() { return { cacheNames: [], diff --git a/chestnut-ui/src/views/monitor/logs/index.vue b/chestnut-ui/src/views/monitor/logs/index.vue index ec031017..d89c2890 100644 --- a/chestnut-ui/src/views/monitor/logs/index.vue +++ b/chestnut-ui/src/views/monitor/logs/index.vue @@ -33,7 +33,7 @@ import { getLogMenus } from "@/api/monitor/logs"; export default { - name: "MonitorLogs", + name: "MonitorLogsIndex", data() { return { loading: true, diff --git a/chestnut-ui/src/views/monitor/logs/logininfo.vue b/chestnut-ui/src/views/monitor/logs/logininfo.vue index d7432c96..7946e5ff 100644 --- a/chestnut-ui/src/views/monitor/logs/logininfo.vue +++ b/chestnut-ui/src/views/monitor/logs/logininfo.vue @@ -130,7 +130,7 @@ import { list, delLogininfor, cleanLogininfor } from "@/api/monitor/logininfor"; export default { - name: "Logininfor", + name: "MonitorLogsLogininfor", dicts: ['SuccessOrFail'], data() { return { diff --git a/chestnut-ui/src/views/monitor/logs/operation.vue b/chestnut-ui/src/views/monitor/logs/operation.vue index 9b310c99..70f8bcbd 100644 --- a/chestnut-ui/src/views/monitor/logs/operation.vue +++ b/chestnut-ui/src/views/monitor/logs/operation.vue @@ -190,7 +190,7 @@ import { list, delOperlog, cleanOperlog } from "@/api/monitor/operlog"; export default { - name: "Operlog", + name: "MonitorLogsOperation", data() { return { // 遮罩层 diff --git a/chestnut-ui/src/views/monitor/online/index.vue b/chestnut-ui/src/views/monitor/online/index.vue index b4a066a6..6d1197b4 100644 --- a/chestnut-ui/src/views/monitor/online/index.vue +++ b/chestnut-ui/src/views/monitor/online/index.vue @@ -67,7 +67,7 @@ import { list, forceLogout } from "@/api/monitor/online"; export default { - name: "Online", + name: "MonitorOnlineIndex", data() { return { // 遮罩层 diff --git a/chestnut-ui/src/views/monitor/server/index.vue b/chestnut-ui/src/views/monitor/server/index.vue index da6a1296..6df75a80 100644 --- a/chestnut-ui/src/views/monitor/server/index.vue +++ b/chestnut-ui/src/views/monitor/server/index.vue @@ -229,7 +229,7 @@ import { getServer } from "@/api/monitor/server"; export default { - name: "Server", + name: "MonitorServerIndex", data() { return { // 服务器信息 diff --git a/chestnut-ui/src/views/monitor/task/index.vue b/chestnut-ui/src/views/monitor/task/index.vue index 9de160d6..e83e1cfc 100644 --- a/chestnut-ui/src/views/monitor/task/index.vue +++ b/chestnut-ui/src/views/monitor/task/index.vue @@ -249,7 +249,7 @@ import { getTaskTypes, listTask, getTask, delTask, addTask, updateTask, enableTask, disableTask, executeTask, getTaskLogs, delTaskLogs } from "@/api/monitor/task"; export default { - name: "ScheduledTask", + name: "MonitorTaskIndex", dicts: ['YesOrNo', 'EnableOrDisable', 'SuccessOrFail'], data() { return { diff --git a/chestnut-ui/src/views/system/config/index.vue b/chestnut-ui/src/views/system/config/index.vue index f38484a1..7c61bf9d 100644 --- a/chestnut-ui/src/views/system/config/index.vue +++ b/chestnut-ui/src/views/system/config/index.vue @@ -164,7 +164,7 @@ import { listConfig, getConfig, delConfig, addConfig, updateConfig, refreshCache import I18nEditor from '@/views/components/I18nFieldEditor'; export default { - name: "Config", + name: "SystemConfigIndex", components: { I18nEditor, }, diff --git a/chestnut-ui/src/views/system/dept/index.vue b/chestnut-ui/src/views/system/dept/index.vue index 209aae1b..e6cff576 100644 --- a/chestnut-ui/src/views/system/dept/index.vue +++ b/chestnut-ui/src/views/system/dept/index.vue @@ -179,7 +179,7 @@ import Treeselect from "@riophae/vue-treeselect"; import "@riophae/vue-treeselect/dist/vue-treeselect.css"; export default { - name: "Dept", + name: "SystemDeptIndex", dicts: ['EnableOrDisable'], components: { Treeselect }, data() { diff --git a/chestnut-ui/src/views/system/dict/index.vue b/chestnut-ui/src/views/system/dict/index.vue index 2d392bdd..b96648ca 100644 --- a/chestnut-ui/src/views/system/dict/index.vue +++ b/chestnut-ui/src/views/system/dict/index.vue @@ -170,7 +170,7 @@ import { listType, getType, delType, addType, updateType, refreshCache } from "@ import I18nEditor from '@/views/components/I18nFieldEditor'; export default { - name: "Dict", + name: "SystemDictIndex", components: { I18nEditor, }, diff --git a/chestnut-ui/src/views/system/groovy/index.vue b/chestnut-ui/src/views/system/groovy/index.vue index 03842135..d8efe3ce 100644 --- a/chestnut-ui/src/views/system/groovy/index.vue +++ b/chestnut-ui/src/views/system/groovy/index.vue @@ -24,7 +24,7 @@ import { executeGroovySrcity } from "@/api/system/groovy"; export default { - name: "SysGroovyScript", + name: "SystemGroovyIndex", data() { return { loading: false, diff --git a/chestnut-ui/src/views/system/i18n/index.vue b/chestnut-ui/src/views/system/i18n/index.vue index a198114f..cf88a79c 100644 --- a/chestnut-ui/src/views/system/i18n/index.vue +++ b/chestnut-ui/src/views/system/i18n/index.vue @@ -167,7 +167,7 @@ import { listI18nDict, getI18nDict, delI18nDict, addI18nDict, updateI18nDict, refreshCache } from "@/api/system/i18nDict"; export default { - name: "I18nDict", + name: "SystemI18nIndex", dicts: [ 'I18nDictType' ], data() { return { diff --git a/chestnut-ui/src/views/system/menu/index.vue b/chestnut-ui/src/views/system/menu/index.vue index c517ecff..184bafb8 100644 --- a/chestnut-ui/src/views/system/menu/index.vue +++ b/chestnut-ui/src/views/system/menu/index.vue @@ -302,7 +302,7 @@ import IconSelect from "@/components/IconSelect"; import I18nEditor from '../../components/I18nFieldEditor'; export default { - name: "Menu", + name: "SystemMenuIndex", dicts: ['YesOrNo', 'EnableOrDisable'], components: { Treeselect, IconSelect, I18nEditor }, data() { diff --git a/chestnut-ui/src/views/system/notice/index.vue b/chestnut-ui/src/views/system/notice/index.vue index 402c6ad6..df3893d0 100644 --- a/chestnut-ui/src/views/system/notice/index.vue +++ b/chestnut-ui/src/views/system/notice/index.vue @@ -180,7 +180,7 @@ import { listNotice, getNotice, delNotice, addNotice, updateNotice } from "@/api/system/notice"; export default { - name: "Notice", + name: "SystemNoticeIndex", dicts: ['NoticeStatus', 'NoticeType'], data() { return { diff --git a/chestnut-ui/src/views/system/post/index.vue b/chestnut-ui/src/views/system/post/index.vue index 6dfb07e1..ed2e8538 100644 --- a/chestnut-ui/src/views/system/post/index.vue +++ b/chestnut-ui/src/views/system/post/index.vue @@ -167,7 +167,7 @@ import { listPost, getPost, delPost, addPost, updatePost } from "@/api/system/post"; export default { - name: "Post", + name: "SystemPostIndex", dicts: ['EnableOrDisable'], data() { return { diff --git a/chestnut-ui/src/views/system/role/index.vue b/chestnut-ui/src/views/system/role/index.vue index 1486a36d..14664c3d 100644 --- a/chestnut-ui/src/views/system/role/index.vue +++ b/chestnut-ui/src/views/system/role/index.vue @@ -214,7 +214,7 @@ import { listRole, getRole, delRole, addRole, updateRole, changeRoleStatus } fro import RolePermission from '@/views/system/permission/permsTab'; export default { - name: "Role", + name: "SystemRoleIndex", dicts: ['EnableOrDisable'], components: { 'role-permission': RolePermission diff --git a/chestnut-ui/src/views/system/security/index.vue b/chestnut-ui/src/views/system/security/index.vue index 32eb6474..ec8f1330 100644 --- a/chestnut-ui/src/views/system/security/index.vue +++ b/chestnut-ui/src/views/system/security/index.vue @@ -182,7 +182,7 @@ import { listSecurityConfigs, getSecurityConfig, addSecurityConfig, saveSecurityConfig, deleteSecurityConfig, changeConfigStatus } from "@/api/system/security"; export default { - name: "SysSecurityConfig", + name: "SysSecurityIndex", dicts: [ "EnableOrDisable", "SecurityPasswordRule", "SecurityPasswordSensitive", "SecurityPasswordRetryStrategy" ], data () { return { diff --git a/chestnut-ui/src/views/system/user/index.vue b/chestnut-ui/src/views/system/user/index.vue index 5f2887e3..12937ef7 100644 --- a/chestnut-ui/src/views/system/user/index.vue +++ b/chestnut-ui/src/views/system/user/index.vue @@ -340,7 +340,7 @@ import "@riophae/vue-treeselect/dist/vue-treeselect.css"; import RolePermission from '@/views/system/permission/permsTab'; export default { - name: "User", + name: "SystemUserIndex", dicts: ['SysUserStatus', 'Gender', 'EnableOrDisable'], components: { Treeselect, diff --git a/chestnut-ui/src/views/system/user/userPreference.vue b/chestnut-ui/src/views/system/user/userPreference.vue index 2f2ca038..9288875c 100644 --- a/chestnut-ui/src/views/system/user/userPreference.vue +++ b/chestnut-ui/src/views/system/user/userPreference.vue @@ -18,8 +18,8 @@ @@ -27,8 +27,8 @@ @@ -36,12 +36,17 @@ + + + + + {{ $t('Common.Save') }} diff --git a/chestnut-ui/src/views/tool/build/index.vue b/chestnut-ui/src/views/tool/build/index.vue index d679422e..01e153bb 100644 --- a/chestnut-ui/src/views/tool/build/index.vue +++ b/chestnut-ui/src/views/tool/build/index.vue @@ -155,6 +155,7 @@ let oldActiveId let tempActiveData export default { + name: "ToolBuildIndex", components: { draggable, render, diff --git a/chestnut-ui/src/views/tool/gen/index.vue b/chestnut-ui/src/views/tool/gen/index.vue index a1631750..f81d4336 100644 --- a/chestnut-ui/src/views/tool/gen/index.vue +++ b/chestnut-ui/src/views/tool/gen/index.vue @@ -201,7 +201,7 @@ hljs.registerLanguage("javascript", require("highlight.js/lib/languages/javascri hljs.registerLanguage("sql", require("highlight.js/lib/languages/sql")); export default { - name: "Gen", + name: "ToolGenIndex", components: { importTable }, data() { return { diff --git a/chestnut-ui/src/views/vote/index.vue b/chestnut-ui/src/views/vote/index.vue index c541024b..f08fb14e 100644 --- a/chestnut-ui/src/views/vote/index.vue +++ b/chestnut-ui/src/views/vote/index.vue @@ -218,7 +218,7 @@ import { codeValidator } from '@/utils/validate' import { getVoteUserTypes, getVoteList, getVoteDetail, addVote, updateVote, deleteVotes } from "@/api/vote/vote"; export default { - name: "VoteList", + name: "VoteIndex", dicts: [ 'VoteStatus', 'VoteViewType' ], components: { },