Compare commits
8 Commits
cf2a372ba4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| d498f9f3cd | |||
| a2f432c70c | |||
| 75fd0077cb | |||
| 4e20c2df62 | |||
| 4478f5f23c | |||
| b007b17e8d | |||
| f472933846 | |||
| ff432b82c0 |
@@ -104,8 +104,14 @@ public class CmsNavigationController extends BaseController {
|
|||||||
}
|
}
|
||||||
// 去除前面空格
|
// 去除前面空格
|
||||||
cmsNavigation.setTitle(StrUtil.trimStart(cmsNavigation.getTitle()));
|
cmsNavigation.setTitle(StrUtil.trimStart(cmsNavigation.getTitle()));
|
||||||
|
// code 规范化:trim + 转小写(唯一性以规范化后的值为准)
|
||||||
|
if (cmsNavigation.getCode() != null) {
|
||||||
|
cmsNavigation.setCode(cmsNavigation.getCode().trim().toLowerCase());
|
||||||
|
}
|
||||||
// 单页模型绑定唯一性校验:同一租户下同一个单页只能绑定一个栏目
|
// 单页模型绑定唯一性校验:同一租户下同一个单页只能绑定一个栏目
|
||||||
checkPageBindingUnique(cmsNavigation);
|
checkPageBindingUnique(cmsNavigation);
|
||||||
|
// 栏目 code 租户内唯一性校验(code 为空/空串时不校验)
|
||||||
|
checkCodeUnique(cmsNavigation);
|
||||||
if (cmsNavigationService.save(cmsNavigation)) {
|
if (cmsNavigationService.save(cmsNavigation)) {
|
||||||
// 添加成功事务处理
|
// 添加成功事务处理
|
||||||
cmsNavigationService.saveAsync(cmsNavigation);
|
cmsNavigationService.saveAsync(cmsNavigation);
|
||||||
@@ -119,8 +125,14 @@ public class CmsNavigationController extends BaseController {
|
|||||||
@Operation(summary = "修改网站导航记录表")
|
@Operation(summary = "修改网站导航记录表")
|
||||||
@PutMapping()
|
@PutMapping()
|
||||||
public ApiResult<?> update(@RequestBody CmsNavigation cmsNavigation) {
|
public ApiResult<?> update(@RequestBody CmsNavigation cmsNavigation) {
|
||||||
|
// code 规范化:trim + 转小写(唯一性以规范化后的值为准)
|
||||||
|
if (cmsNavigation.getCode() != null) {
|
||||||
|
cmsNavigation.setCode(cmsNavigation.getCode().trim().toLowerCase());
|
||||||
|
}
|
||||||
// 单页模型绑定唯一性校验:同一租户下同一个单页只能绑定一个栏目
|
// 单页模型绑定唯一性校验:同一租户下同一个单页只能绑定一个栏目
|
||||||
checkPageBindingUnique(cmsNavigation);
|
checkPageBindingUnique(cmsNavigation);
|
||||||
|
// 栏目 code 租户内唯一性校验(code 为空/空串时不校验)
|
||||||
|
checkCodeUnique(cmsNavigation);
|
||||||
// 顶级栏目(parentId=0)的显示位置(top/bottom)发生变更时,
|
// 顶级栏目(parentId=0)的显示位置(top/bottom)发生变更时,
|
||||||
// 递归同步所有子级(含孙级)的 top/bottom,保持层级一致。
|
// 递归同步所有子级(含孙级)的 top/bottom,保持层级一致。
|
||||||
if (cmsNavigation.getParentId() != null && cmsNavigation.getParentId() == 0
|
if (cmsNavigation.getParentId() != null && cmsNavigation.getParentId() == 0
|
||||||
@@ -160,6 +172,27 @@ public class CmsNavigationController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('cms:cmsNavigation:update')")
|
@PreAuthorize("hasAuthority('cms:cmsNavigation:update')")
|
||||||
|
/**
|
||||||
|
* 校验栏目 code 租户内唯一:同一租户下 code 不可重复(code 为空/空串时不校验)。
|
||||||
|
* 与单页绑定唯一性校验并列,避免触发 uk_navigation_tenant_code 唯一索引导致用户看到"操作失败"。
|
||||||
|
*/
|
||||||
|
private void checkCodeUnique(CmsNavigation cmsNavigation) {
|
||||||
|
String code = cmsNavigation.getCode();
|
||||||
|
if (code == null || code.trim().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LambdaQueryWrapper<CmsNavigation> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(CmsNavigation::getTenantId, getTenantId())
|
||||||
|
.eq(CmsNavigation::getCode, code.trim())
|
||||||
|
.eq(CmsNavigation::getDeleted, 0);
|
||||||
|
if (cmsNavigation.getNavigationId() != null) {
|
||||||
|
wrapper.ne(CmsNavigation::getNavigationId, cmsNavigation.getNavigationId());
|
||||||
|
}
|
||||||
|
if (cmsNavigationService.count(wrapper) > 0) {
|
||||||
|
throw new BusinessException("栏目 code「" + code.trim() + "」已存在,请更换");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Operation(summary = "修改栏目状态")
|
@Operation(summary = "修改栏目状态")
|
||||||
@PutMapping("/status")
|
@PutMapping("/status")
|
||||||
public ApiResult<?> updateStatus(@RequestBody Map<String, Object> body) {
|
public ApiResult<?> updateStatus(@RequestBody Map<String, Object> body) {
|
||||||
|
|||||||
@@ -109,6 +109,18 @@ public class CmsWebsiteController extends BaseController {
|
|||||||
// 记录当前登录用户id
|
// 记录当前登录用户id
|
||||||
User loginUser = getLoginUser();
|
User loginUser = getLoginUser();
|
||||||
if (loginUser != null) {
|
if (loginUser != null) {
|
||||||
|
// 防重复建站:同一租户已存在站点时拒绝新增,
|
||||||
|
// 避免并发请求/重复点击建出多条记录(getSiteInfo 仅取 limit 1,多余记录会成为孤儿数据)
|
||||||
|
if (ObjectUtil.isNotEmpty(loginUser.getTenantId())) {
|
||||||
|
long exist = cmsWebsiteService.count(
|
||||||
|
new LambdaQueryWrapper<CmsWebsite>()
|
||||||
|
.eq(CmsWebsite::getTenantId, loginUser.getTenantId())
|
||||||
|
.eq(CmsWebsite::getDeleted, 0)
|
||||||
|
);
|
||||||
|
if (exist > 0) {
|
||||||
|
return fail("当前租户已存在站点,请勿重复创建");
|
||||||
|
}
|
||||||
|
}
|
||||||
cmsWebsite.setLoginUser(loginUser);
|
cmsWebsite.setLoginUser(loginUser);
|
||||||
return success("创建成功", cmsWebsiteService.create(cmsWebsite));
|
return success("创建成功", cmsWebsiteService.create(cmsWebsite));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.gxwebsoft.cms.controller;
|
package com.gxwebsoft.cms.controller;
|
||||||
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
||||||
import com.gxwebsoft.common.core.web.BaseController;
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
import com.gxwebsoft.cms.service.CmsWebsiteSettingService;
|
import com.gxwebsoft.cms.service.CmsWebsiteSettingService;
|
||||||
import com.gxwebsoft.cms.entity.CmsWebsiteSetting;
|
import com.gxwebsoft.cms.entity.CmsWebsiteSetting;
|
||||||
@@ -9,6 +8,7 @@ import com.gxwebsoft.cms.param.CmsWebsiteSettingParam;
|
|||||||
import com.gxwebsoft.common.core.web.ApiResult;
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
import com.gxwebsoft.common.core.web.BatchParam;
|
import com.gxwebsoft.common.core.web.BatchParam;
|
||||||
|
import com.gxwebsoft.cms.mapper.CmsWebsiteSettingMapper;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
@@ -30,6 +30,9 @@ public class CmsWebsiteSettingController extends BaseController {
|
|||||||
@Resource
|
@Resource
|
||||||
private CmsWebsiteSettingService cmsWebsiteSettingService;
|
private CmsWebsiteSettingService cmsWebsiteSettingService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CmsWebsiteSettingMapper cmsWebsiteSettingMapper;
|
||||||
|
|
||||||
@Operation(summary = "分页查询网站设置")
|
@Operation(summary = "分页查询网站设置")
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
public ApiResult<PageResult<CmsWebsiteSetting>> page(CmsWebsiteSettingParam param) {
|
public ApiResult<PageResult<CmsWebsiteSetting>> page(CmsWebsiteSettingParam param) {
|
||||||
@@ -47,18 +50,26 @@ public class CmsWebsiteSettingController extends BaseController {
|
|||||||
@Operation(summary = "根据id查询网站设置")
|
@Operation(summary = "根据id查询网站设置")
|
||||||
@GetMapping("/{id}")
|
@GetMapping("/{id}")
|
||||||
public ApiResult<CmsWebsiteSetting> get(@PathVariable("id") Integer id) {
|
public ApiResult<CmsWebsiteSetting> get(@PathVariable("id") Integer id) {
|
||||||
// 使用关联查询
|
// 自定义查询忽略逻辑删除:能捞到 deleted IS NULL 的孤儿记录(旧逻辑遗留),
|
||||||
final CmsWebsiteSetting cmsWebsiteSetting = cmsWebsiteSettingService.getOne(new LambdaQueryWrapper<CmsWebsiteSetting>().eq(CmsWebsiteSetting::getWebsiteId, id));
|
// 避免“查不到→新建”时撞 website_id 唯一索引,导致前端报“未获取到网站设置记录”。
|
||||||
if(ObjectUtil.isEmpty(cmsWebsiteSetting)){
|
final CmsWebsiteSetting existing = cmsWebsiteSettingMapper.selectByWebsiteIdIgnoreLogic(id);
|
||||||
|
if (ObjectUtil.isEmpty(existing)) {
|
||||||
|
// 真正没有记录才新建,并显式 setDeleted(0),避免被 @TableLogic 查询过滤。
|
||||||
final CmsWebsiteSetting setting = new CmsWebsiteSetting();
|
final CmsWebsiteSetting setting = new CmsWebsiteSetting();
|
||||||
setting.setWebsiteId(id);
|
setting.setWebsiteId(id);
|
||||||
|
setting.setDeleted(0);
|
||||||
cmsWebsiteSettingService.save(setting);
|
cmsWebsiteSettingService.save(setting);
|
||||||
return success(cmsWebsiteSettingService.getOne(new LambdaQueryWrapper<CmsWebsiteSetting>().eq(CmsWebsiteSetting::getWebsiteId, id)));
|
return success(setting);
|
||||||
}
|
}
|
||||||
return success(cmsWebsiteSetting);
|
// 查到的是孤儿/已删除记录:复活为正常态(deleted=0)后返回,不再重复插入。
|
||||||
|
if (existing.getDeleted() == null || existing.getDeleted() != 0) {
|
||||||
|
cmsWebsiteSettingMapper.reviveById(existing.getId());
|
||||||
|
existing.setDeleted(0);
|
||||||
|
}
|
||||||
|
return success(existing);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('cms:cmsWebsiteSetting:save')")
|
@PreAuthorize("hasAuthority('cms:website:save')")
|
||||||
@Operation(summary = "添加网站设置")
|
@Operation(summary = "添加网站设置")
|
||||||
@PostMapping()
|
@PostMapping()
|
||||||
public ApiResult<?> save(@RequestBody CmsWebsiteSetting cmsWebsiteSetting) {
|
public ApiResult<?> save(@RequestBody CmsWebsiteSetting cmsWebsiteSetting) {
|
||||||
@@ -78,7 +89,7 @@ public class CmsWebsiteSettingController extends BaseController {
|
|||||||
return fail("修改失败");
|
return fail("修改失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('cms:cmsWebsiteSetting:remove')")
|
@PreAuthorize("hasAuthority('cms:website:remove')")
|
||||||
@Operation(summary = "删除网站设置")
|
@Operation(summary = "删除网站设置")
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public ApiResult<?> remove(@PathVariable("id") Integer id) {
|
public ApiResult<?> remove(@PathVariable("id") Integer id) {
|
||||||
@@ -88,7 +99,7 @@ public class CmsWebsiteSettingController extends BaseController {
|
|||||||
return fail("删除失败");
|
return fail("删除失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('cms:cmsWebsiteSetting:save')")
|
@PreAuthorize("hasAuthority('cms:website:save')")
|
||||||
@Operation(summary = "批量添加网站设置")
|
@Operation(summary = "批量添加网站设置")
|
||||||
@PostMapping("/batch")
|
@PostMapping("/batch")
|
||||||
public ApiResult<?> saveBatch(@RequestBody List<CmsWebsiteSetting> list) {
|
public ApiResult<?> saveBatch(@RequestBody List<CmsWebsiteSetting> list) {
|
||||||
@@ -98,7 +109,7 @@ public class CmsWebsiteSettingController extends BaseController {
|
|||||||
return fail("添加失败");
|
return fail("添加失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('cms:cmsWebsiteSetting:update')")
|
@PreAuthorize("hasAuthority('cms:website:update')")
|
||||||
@Operation(summary = "批量修改网站设置")
|
@Operation(summary = "批量修改网站设置")
|
||||||
@PutMapping("/batch")
|
@PutMapping("/batch")
|
||||||
public ApiResult<?> removeBatch(@RequestBody BatchParam<CmsWebsiteSetting> batchParam) {
|
public ApiResult<?> removeBatch(@RequestBody BatchParam<CmsWebsiteSetting> batchParam) {
|
||||||
@@ -108,7 +119,7 @@ public class CmsWebsiteSettingController extends BaseController {
|
|||||||
return fail("修改失败");
|
return fail("修改失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('cms:cmsWebsiteSetting:remove')")
|
@PreAuthorize("hasAuthority('cms:website:remove')")
|
||||||
@Operation(summary = "批量删除网站设置")
|
@Operation(summary = "批量删除网站设置")
|
||||||
@DeleteMapping("/batch")
|
@DeleteMapping("/batch")
|
||||||
public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
|
public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
|
||||||
|
|||||||
@@ -109,6 +109,16 @@ public class CmsArticle implements Serializable {
|
|||||||
@Schema(description = "实际阅读量")
|
@Schema(description = "实际阅读量")
|
||||||
private Integer actualViews;
|
private Integer actualViews;
|
||||||
|
|
||||||
|
@Schema(description = "阅读量(实际阅读量 + 虚拟阅读量,仅用于展示)")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Integer views;
|
||||||
|
|
||||||
|
public Integer getViews() {
|
||||||
|
int actual = actualViews == null ? 0 : actualViews;
|
||||||
|
int virtual = virtualViews == null ? 0 : virtualViews;
|
||||||
|
return actual + virtual;
|
||||||
|
}
|
||||||
|
|
||||||
@Schema(description = "评分")
|
@Schema(description = "评分")
|
||||||
private BigDecimal rate;
|
private BigDecimal rate;
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ public class CmsWebsite implements Serializable {
|
|||||||
@Schema(description = "网站LOGO(深色模式)")
|
@Schema(description = "网站LOGO(深色模式)")
|
||||||
private String websiteDarkLogo;
|
private String websiteDarkLogo;
|
||||||
|
|
||||||
|
@Schema(description = "网站图标/正方形Logo(后台应用卡/分享图标/浏览器标签页图标)")
|
||||||
|
private String websiteAvatar;
|
||||||
|
|
||||||
@Schema(description = "网站类型")
|
@Schema(description = "网站类型")
|
||||||
private String websiteType;
|
private String websiteType;
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ public class CmsWebsiteSetting implements Serializable {
|
|||||||
@Schema(description = "导航栏最多显示数量")
|
@Schema(description = "导航栏最多显示数量")
|
||||||
private Boolean maxMenuNum;
|
private Boolean maxMenuNum;
|
||||||
|
|
||||||
|
@Schema(description = "首页优势模块配置(JSON)")
|
||||||
|
private String features;
|
||||||
|
|
||||||
@Schema(description = "排序号")
|
@Schema(description = "排序号")
|
||||||
private Integer sortNumber;
|
private Integer sortNumber;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
|||||||
import com.gxwebsoft.cms.entity.CmsWebsiteSetting;
|
import com.gxwebsoft.cms.entity.CmsWebsiteSetting;
|
||||||
import com.gxwebsoft.cms.param.CmsWebsiteSettingParam;
|
import com.gxwebsoft.cms.param.CmsWebsiteSettingParam;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
import org.apache.ibatis.annotations.Update;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -34,4 +36,20 @@ public interface CmsWebsiteSettingMapper extends BaseMapper<CmsWebsiteSetting> {
|
|||||||
*/
|
*/
|
||||||
List<CmsWebsiteSetting> selectListRel(@Param("param") CmsWebsiteSettingParam param);
|
List<CmsWebsiteSetting> selectListRel(@Param("param") CmsWebsiteSettingParam param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 忽略逻辑删除,按 websiteId 查最新一条记录。
|
||||||
|
* 用于「根据id查询」自愈:能捞到 deleted IS NULL 的孤儿记录(旧逻辑遗留),
|
||||||
|
* 避免 MP 自带 getOne 因补 deleted=0 而查不到、继而新建时撞 website_id 唯一索引。
|
||||||
|
* 注意:自定义 @Select 不被 @TableLogic 自动追加 deleted 过滤(与本项目 XML 自定义查询一致)。
|
||||||
|
*/
|
||||||
|
@Select("SELECT * FROM cms_website_setting WHERE website_id = #{websiteId} ORDER BY id ASC LIMIT 1")
|
||||||
|
CmsWebsiteSetting selectByWebsiteIdIgnoreLogic(@Param("websiteId") Integer websiteId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将指定记录复活为正常态(deleted=0)。
|
||||||
|
* 自定义 @Update 不受 @TableLogic 改写,可修正孤儿记录而不被 WHERE deleted=0 拦截。
|
||||||
|
*/
|
||||||
|
@Update("UPDATE cms_website_setting SET deleted = 0 WHERE id = #{id}")
|
||||||
|
void reviveById(@Param("id") Integer id);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,9 +112,15 @@ public class CmsArticleServiceImpl extends ServiceImpl<CmsArticleMapper, CmsArti
|
|||||||
param.setArticleId(articleId);
|
param.setArticleId(articleId);
|
||||||
final CmsArticle article = param.getOne(baseMapper.selectListRel(param));
|
final CmsArticle article = param.getOne(baseMapper.selectListRel(param));
|
||||||
if (ObjectUtil.isNotEmpty(article)) {
|
if (ObjectUtil.isNotEmpty(article)) {
|
||||||
// 更新阅读数量
|
// 原子递增实际阅读量(COALESCE 兼容历史空值,避免并发覆盖)
|
||||||
article.setActualViews(article.getActualViews() + 1);
|
boolean viewUpdated = lambdaUpdate()
|
||||||
updateById(article);
|
.setSql("actual_views = COALESCE(actual_views, 0) + 1")
|
||||||
|
.eq(CmsArticle::getArticleId, article.getArticleId())
|
||||||
|
.update();
|
||||||
|
if (viewUpdated) {
|
||||||
|
int current = article.getActualViews() == null ? 0 : article.getActualViews();
|
||||||
|
article.setActualViews(current + 1);
|
||||||
|
}
|
||||||
// 读取Banner
|
// 读取Banner
|
||||||
// final CmsModel model = cmsModelService.getOne(new LambdaQueryWrapper<CmsModel>().eq(CmsModel::getModel, article.getModel()).last("limit 1"));
|
// final CmsModel model = cmsModelService.getOne(new LambdaQueryWrapper<CmsModel>().eq(CmsModel::getModel, article.getModel()).last("limit 1"));
|
||||||
// if (ObjectUtil.isNotEmpty(model)) {
|
// if (ObjectUtil.isNotEmpty(model)) {
|
||||||
|
|||||||
@@ -193,6 +193,15 @@ public class CmsWebsiteServiceImpl extends ServiceImpl<CmsWebsiteMapper, CmsWebs
|
|||||||
website.setTemplateId(loginUser.getTemplateId());
|
website.setTemplateId(loginUser.getTemplateId());
|
||||||
website.setCompanyId(loginUser.getCompanyId());
|
website.setCompanyId(loginUser.getCompanyId());
|
||||||
|
|
||||||
|
// 默认运行中:status(后台枚举)与 running(getSiteInfo 状态展示用)保持一致,
|
||||||
|
// 避免公开站读取到空值显示「状态未知」;调用方可覆盖
|
||||||
|
if (website.getStatus() == null) {
|
||||||
|
website.setStatus(1);
|
||||||
|
}
|
||||||
|
if (website.getRunning() == null) {
|
||||||
|
website.setRunning(1);
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化数据
|
// 初始化数据
|
||||||
if(save(website)){
|
if(save(website)){
|
||||||
// 插入网站设置记录
|
// 插入网站设置记录
|
||||||
|
|||||||
Reference in New Issue
Block a user