fix(cms): 增强单页绑定唯一性校验和缓存失效处理

- 在CmsNavigationController新增checkPageBindingUnique方法,确保同一租户下同一单页只能绑定一个栏目
- 在新增和修改网站导航时调用绑定唯一性校验,避免唯一索引冲突导致操作失败
- 在CmsWebsiteController修改后立即清理站点信息缓存,避免缓存数据延迟导致旧数据读取
- clearSiteInfoCacheQuietly方法中捕获异常,确保缓存清理失败不会影响主流程
- 添加了缓存清理的日志记录,方便问题追踪
This commit is contained in:
2026-07-30 22:22:23 +08:00
parent 5b6e1be35b
commit db680d610d
2 changed files with 47 additions and 0 deletions

View File

@@ -21,6 +21,7 @@ import com.gxwebsoft.cms.param.CmsNavigationParam;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.core.exception.BusinessException;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.common.system.service.UserService;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -103,6 +104,8 @@ public class CmsNavigationController extends BaseController {
}
// 去除前面空格
cmsNavigation.setTitle(StrUtil.trimStart(cmsNavigation.getTitle()));
// 单页模型绑定唯一性校验:同一租户下同一个单页只能绑定一个栏目
checkPageBindingUnique(cmsNavigation);
if (cmsNavigationService.save(cmsNavigation)) {
// 添加成功事务处理
cmsNavigationService.saveAsync(cmsNavigation);
@@ -116,6 +119,8 @@ public class CmsNavigationController extends BaseController {
@Operation(summary = "修改网站导航记录表")
@PutMapping()
public ApiResult<?> update(@RequestBody CmsNavigation cmsNavigation) {
// 单页模型绑定唯一性校验:同一租户下同一个单页只能绑定一个栏目
checkPageBindingUnique(cmsNavigation);
if (cmsNavigationService.updateById(cmsNavigation)) {
// 修改成功事务处理
cmsNavigationService.saveAsync(cmsNavigation);
@@ -281,6 +286,26 @@ public class CmsNavigationController extends BaseController {
}
}
/**
* 校验单页绑定唯一性同一租户下同一个单页pageId只能绑定一个栏目
* 避免触发 cms_navigation.uk_navigation_tenant_page 唯一索引导致用户看到"操作失败"。
*/
private void checkPageBindingUnique(CmsNavigation cmsNavigation) {
if (!"page".equals(cmsNavigation.getModel()) || cmsNavigation.getPageId() == null) {
return;
}
LambdaQueryWrapper<CmsNavigation> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CmsNavigation::getTenantId, getTenantId())
.eq(CmsNavigation::getPageId, cmsNavigation.getPageId())
.eq(CmsNavigation::getDeleted, 0);
if (cmsNavigation.getNavigationId() != null) {
wrapper.ne(CmsNavigation::getNavigationId, cmsNavigation.getNavigationId());
}
if (cmsNavigationService.count(wrapper) > 0) {
throw new BusinessException("该单页已被其他栏目绑定,请先解绑后再绑定");
}
}
/**
* 递归创建子级导航
*/

View File

@@ -134,6 +134,9 @@ public class CmsWebsiteController extends BaseController {
cmsWebsite.setWebsiteCode(null);
}
if (cmsWebsiteService.updateById(cmsWebsite)) {
// 站点信息(含 domain / templateId 等)有 1 天 Redis 缓存,改完必须立刻失效,
// 否则公开站与后台链接拼接最长 1 天读到旧值(与 CmsNavigationController 写操作保持一致)。
clearSiteInfoCacheQuietly();
return success("修改成功");
}
return fail("修改失败");
@@ -144,11 +147,30 @@ public class CmsWebsiteController extends BaseController {
@PutMapping("/updateAll")
public ApiResult<?> updateAll(@RequestBody CmsWebsite cmsWebsite) {
if (cmsWebsiteService.updateByIdAll(cmsWebsite)) {
clearSiteInfoCacheQuietly();
return success("修改成功");
}
return fail("修改失败");
}
/**
* 清除当前租户的站点信息缓存。
* 缓存失效属于写操作的附带动作,异常不应影响主流程(更新本身已成功)。
*/
private void clearSiteInfoCacheQuietly() {
try {
Integer tenantId = getTenantId();
if (ObjectUtil.isEmpty(tenantId)) {
return;
}
redisUtil.delete(SITE_INFO_KEY_PREFIX.concat(tenantId.toString()));
redisUtil.delete(MP_INFO_KEY_PREFIX.concat(tenantId.toString()));
log.info("已清除站点信息缓存租户ID: {}", tenantId);
} catch (Exception e) {
log.warn("清除站点信息缓存失败: {}", e.getMessage());
}
}
@PreAuthorize("hasAuthority('cms:website:remove')")
@Operation(summary = "删除网站信息记录表")
@DeleteMapping("/{id}")