feat(cms): 增强单页与导航栏目的一对一绑定支持
- CmsNavigation 中新增绑定单页的访问路径、标题和状态字段,完善 VO 透传 - CmsPage 新增 navigationId 字段,用于关联导航栏目,支持按导航过滤单页 - Mapper XML 调整,CmsNavigation 查询添加 cms_page 相关字段联表查询 - CmsPage 查询添加左联导航栏目,支持按导航 ID 过滤 - CmsPageParam 新增 navigationId 查询参数支持 - CmsPageServiceImpl 增加保存、更新单页时的导航绑定逻辑,实现单页与导航的唯一绑定关系维护 - 删除单页时,先清理所有指向该单页的导航绑定,确保数据一致性 - bindNavigation 方法实现解绑与换绑逻辑,保证导航与单页保持一对一关系 - 所有单页导航绑定相关操作均添加事务支持,确保操作原子性
This commit is contained in:
@@ -121,9 +121,21 @@ public class CmsNavigation implements Serializable {
|
||||
@TableField(exist = false)
|
||||
private Integer parentPosition;
|
||||
|
||||
@Schema(description = "绑定的页面(已废弃)")
|
||||
@Schema(description = "绑定的单页ID(cms_page.page_id),单页管理页「关联导航」写入,与单页一一对应")
|
||||
private Integer pageId;
|
||||
|
||||
@Schema(description = "绑定单页的访问路径slug(VO 透传,来自 cms_page.path)")
|
||||
@TableField(exist = false)
|
||||
private String pagePath;
|
||||
|
||||
@Schema(description = "绑定单页的标题(VO 透传,来自 cms_page.title)")
|
||||
@TableField(exist = false)
|
||||
private String pageTitle;
|
||||
|
||||
@Schema(description = "绑定单页的状态 0草稿1已发布2已下线(VO 透传,来自 cms_page.status)")
|
||||
@TableField(exist = false)
|
||||
private Integer pageStatus;
|
||||
|
||||
@Schema(description = "详情页ID")
|
||||
private Integer itemId;
|
||||
|
||||
|
||||
@@ -86,6 +86,10 @@ public class CmsPage implements Serializable {
|
||||
@TableField(exist = false)
|
||||
private String statusText;
|
||||
|
||||
@Schema(description = "关联的导航栏目ID(cms_navigation.navigation_id),存于 cms_navigation.page_id;本实体不持久化该字段,仅作为 VO 透传与写回导航的入参")
|
||||
@TableField(exist = false)
|
||||
private Integer navigationId;
|
||||
|
||||
public String getStatusText() {
|
||||
if (this.status == null) return "";
|
||||
switch (this.status) {
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
|
||||
<!-- 关联查询sql -->
|
||||
<sql id="selectSql">
|
||||
SELECT a.*, b.title as parentName, b.position as parentPosition, c.name as modelName
|
||||
SELECT a.*, b.title as parentName, b.position as parentPosition, c.name as modelName,
|
||||
p.path AS pagePath, p.title AS pageTitle, p.status AS pageStatus
|
||||
FROM cms_navigation a
|
||||
LEFT JOIN cms_navigation b ON a.parent_id = b.navigation_id AND b.deleted = 0 AND b.tenant_id = a.tenant_id
|
||||
<!-- c.model = 0 会触发 MySQL 字符串转数字比较,导致几乎所有模型都匹配,从而把导航行成倍放大 -->
|
||||
LEFT JOIN cms_model c ON a.model = c.model AND c.deleted = 0 AND c.tenant_id = a.tenant_id
|
||||
LEFT JOIN cms_page p ON p.page_id = a.page_id AND p.deleted = 0
|
||||
<where>
|
||||
<if test="param.navigationId != null">
|
||||
AND a.navigation_id = #{param.navigationId}
|
||||
|
||||
@@ -4,12 +4,16 @@
|
||||
|
||||
<!-- 单页查询sql -->
|
||||
<sql id="selectSql">
|
||||
SELECT a.*
|
||||
SELECT a.*, n.navigation_id AS navigationId
|
||||
FROM cms_page a
|
||||
LEFT JOIN cms_navigation n ON n.page_id = a.page_id AND n.deleted = 0 AND n.tenant_id = a.tenant_id
|
||||
<where>
|
||||
<if test="param.pageId != null">
|
||||
AND a.page_id = #{param.pageId}
|
||||
</if>
|
||||
<if test="param.navigationId != null">
|
||||
AND n.navigation_id = #{param.navigationId}
|
||||
</if>
|
||||
<if test="param.title != null">
|
||||
AND a.title LIKE CONCAT('%', #{param.title}, '%')
|
||||
</if>
|
||||
|
||||
@@ -49,4 +49,9 @@ public class CmsPageParam extends BaseParam {
|
||||
@Schema(description = "单页ID集查询")
|
||||
@TableField(exist = false)
|
||||
private java.util.Set<Integer> pageIds;
|
||||
|
||||
@Schema(description = "关联的导航栏目ID(cms_navigation.navigation_id),用于按导航过滤单页")
|
||||
@TableField(exist = false)
|
||||
@QueryField(type = QueryType.EQ)
|
||||
private Integer navigationId;
|
||||
}
|
||||
|
||||
@@ -4,15 +4,19 @@ import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.gxwebsoft.cms.entity.CmsNavigation;
|
||||
import com.gxwebsoft.cms.entity.CmsPage;
|
||||
import com.gxwebsoft.cms.mapper.CmsNavigationMapper;
|
||||
import com.gxwebsoft.cms.mapper.CmsPageMapper;
|
||||
import com.gxwebsoft.cms.param.CmsPageParam;
|
||||
import com.gxwebsoft.cms.service.CmsPageService;
|
||||
import com.gxwebsoft.common.core.web.PageParam;
|
||||
import com.gxwebsoft.common.core.web.PageResult;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -24,6 +28,9 @@ import java.util.List;
|
||||
@Service
|
||||
public class CmsPageServiceImpl extends com.baomidou.mybatisplus.extension.service.impl.ServiceImpl<CmsPageMapper, CmsPage> implements CmsPageService {
|
||||
|
||||
@Resource
|
||||
private CmsNavigationMapper cmsNavigationMapper;
|
||||
|
||||
@Override
|
||||
public PageResult<CmsPage> pageRel(CmsPageParam param) {
|
||||
PageParam<CmsPage, CmsPageParam> page = new PageParam<>(param);
|
||||
@@ -72,6 +79,7 @@ public class CmsPageServiceImpl extends com.baomidou.mybatisplus.extension.servi
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean saveRel(CmsPage page) {
|
||||
if (StrUtil.isBlank(page.getPath())) {
|
||||
return false;
|
||||
@@ -88,10 +96,15 @@ public class CmsPageServiceImpl extends com.baomidou.mybatisplus.extension.servi
|
||||
if (page.getViews() == null) {
|
||||
page.setViews(0);
|
||||
}
|
||||
return save(page);
|
||||
boolean saved = save(page);
|
||||
if (saved && page.getNavigationId() != null) {
|
||||
bindNavigation(page.getPageId(), page.getNavigationId(), page.getTenantId());
|
||||
}
|
||||
return saved;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean updateByIdRel(CmsPage page) {
|
||||
if (page.getPageId() == null) {
|
||||
return false;
|
||||
@@ -102,6 +115,49 @@ public class CmsPageServiceImpl extends com.baomidou.mybatisplus.extension.servi
|
||||
if (page.getSortNumber() == null) {
|
||||
page.setSortNumber(0);
|
||||
}
|
||||
return updateById(page);
|
||||
boolean updated = updateById(page);
|
||||
if (updated) {
|
||||
bindNavigation(page.getPageId(), page.getNavigationId(), page.getTenantId());
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean removeById(Serializable id) {
|
||||
// 先清空所有指向该单页的导航引用(navigation.page_id = NULL),再逻辑删除单页
|
||||
cmsNavigationMapper.update(null, Wrappers.lambdaUpdate(CmsNavigation.class)
|
||||
.eq(CmsNavigation::getPageId, (Integer) id)
|
||||
.set(CmsNavigation::getPageId, null));
|
||||
return super.removeById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 维护「单页 ↔ 导航」的一一对应关系:
|
||||
* 1) navigationId 为 null → 解绑(清掉所有绑了该 page 的导航引用)
|
||||
* 2) 否则:先清目标导航此前绑定的其他单页、再清本单页此前绑定的其他导航,最后建立新绑定
|
||||
* 依赖 UNIQUE(tenant_id, page_id) 保证一个 page 只被一个导航绑;
|
||||
* navigationId 为单值列(每个导航最多一个 page_id),天然保证一个导航只绑一篇单页。
|
||||
*/
|
||||
private void bindNavigation(Integer pageId, Integer navigationId, Integer tenantId) {
|
||||
if (navigationId == null) {
|
||||
cmsNavigationMapper.update(null, Wrappers.lambdaUpdate(CmsNavigation.class)
|
||||
.eq(CmsNavigation::getPageId, pageId)
|
||||
.set(CmsNavigation::getPageId, null));
|
||||
return;
|
||||
}
|
||||
// 清掉目标导航此前绑定的其他单页(保证一个导航只绑一篇单页)
|
||||
cmsNavigationMapper.update(null, Wrappers.lambdaUpdate(CmsNavigation.class)
|
||||
.eq(CmsNavigation::getNavigationId, navigationId)
|
||||
.set(CmsNavigation::getPageId, null));
|
||||
// 清掉本单页此前绑定的其他导航(换绑场景)
|
||||
cmsNavigationMapper.update(null, Wrappers.lambdaUpdate(CmsNavigation.class)
|
||||
.eq(CmsNavigation::getPageId, pageId)
|
||||
.set(CmsNavigation::getPageId, null));
|
||||
// 建立新绑定
|
||||
cmsNavigationMapper.update(null, Wrappers.lambdaUpdate(CmsNavigation.class)
|
||||
.eq(CmsNavigation::getNavigationId, navigationId)
|
||||
.eq(CmsNavigation::getTenantId, tenantId)
|
||||
.set(CmsNavigation::getPageId, pageId));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user