feat:新增门户管理功能

This commit is contained in:
hewencai
2025-11-13 10:28:19 +08:00
parent c399bdf720
commit e00086c6e8
12 changed files with 817 additions and 0 deletions

View File

@@ -212,4 +212,7 @@ public interface ErrorCodeConstants {
// ========== 系统序列号记录 1-002-032-000 ========== // ========== 系统序列号记录 1-002-032-000 ==========
ErrorCode SEQUENCE_RECORD_NOT_EXISTS = new ErrorCode(1_002_032_000, "系统序列号记录不存在"); ErrorCode SEQUENCE_RECORD_NOT_EXISTS = new ErrorCode(1_002_032_000, "系统序列号记录不存在");
// ========== 门户网站 1-002-033-000 ==========
ErrorCode PORTAL_NOT_EXISTS = new ErrorCode(1_002_033_000, "门户不存在");
} }

View File

@@ -0,0 +1,105 @@
package com.zt.plat.module.system.controller.admin.portal;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.excel.core.util.ExcelUtils;
import com.zt.plat.module.system.controller.admin.portal.vo.PortalPageReqVO;
import com.zt.plat.module.system.controller.admin.portal.vo.PortalRespVO;
import com.zt.plat.module.system.controller.admin.portal.vo.PortalSaveReqVO;
import com.zt.plat.module.system.dal.dataobject.portal.PortalDO;
import com.zt.plat.module.system.service.portal.PortalService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
/**
* 门户网站 Controller
*
* @author 中铜数字供应链平台
*/
@Tag(name = "管理后台 - 门户网站")
@RestController
@RequestMapping("/system/portal")
@Validated
public class PortalController {
@Resource
private PortalService portalService;
@PostMapping("/create")
@Operation(summary = "创建门户网站")
@PreAuthorize("@ss.hasPermission('system:portal:create')")
public CommonResult<Long> createPortal(@Valid @RequestBody PortalSaveReqVO createReqVO) {
return success(portalService.createPortal(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新门户网站")
@PreAuthorize("@ss.hasPermission('system:portal:update')")
public CommonResult<Boolean> updatePortal(@Valid @RequestBody PortalSaveReqVO updateReqVO) {
portalService.updatePortal(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除门户网站")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:portal:delete')")
public CommonResult<Boolean> deletePortal(@RequestParam("id") Long id) {
portalService.deletePortal(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得门户网站")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:portal:query')")
public CommonResult<PortalRespVO> getPortal(@RequestParam("id") Long id) {
PortalDO portal = portalService.getPortal(id);
return success(BeanUtils.toBean(portal, PortalRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得门户网站分页")
@PreAuthorize("@ss.hasPermission('system:portal:query')")
public CommonResult<PageResult<PortalRespVO>> getPortalPage(@Valid PortalPageReqVO pageReqVO) {
PageResult<PortalDO> pageResult = portalService.getPortalPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, PortalRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出门户网站 Excel")
@PreAuthorize("@ss.hasPermission('system:portal:export')")
public void exportPortalExcel(@Valid PortalPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
List<PortalDO> list = portalService.getPortalPage(pageReqVO).getList();
// 导出 Excel
List<PortalRespVO> data = BeanUtils.toBean(list, PortalRespVO.class);
ExcelUtils.write(response, "门户网站.xls", "数据", PortalRespVO.class, data);
}
/**
* 获取当前用户可访问的门户列表
* 此接口无需权限验证,因为已经通过登录验证,
* 返回的门户列表已经根据用户权限进行了过滤
*/
@GetMapping("/list")
@Operation(summary = "获取我的门户列表")
public CommonResult<List<PortalRespVO>> getMyPortalList() {
Long userId = getLoginUserId();
List<PortalDO> portals = portalService.getPortalListByUserId(userId);
return success(BeanUtils.toBean(portals, PortalRespVO.class));
}
}

View File

@@ -0,0 +1,38 @@
package com.zt.plat.module.system.controller.admin.portal.vo;
import com.zt.plat.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 门户网站分页查询 Request VO
*
* @author 中铜数字供应链平台
*/
@Schema(description = "管理后台 - 门户网站分页查询 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class PortalPageReqVO extends PageParam {
@Schema(description = "门户名称", example = "采购")
private String name;
@Schema(description = "状态", example = "0")
private Integer status;
@Schema(description = "门户分类", example = "业务系统")
private String category;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,73 @@
package com.zt.plat.module.system.controller.admin.portal.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 门户网站 Response VO
*
* @author 中铜数字供应链平台
*/
@Schema(description = "管理后台 - 门户网站 Response VO")
@Data
@ExcelIgnoreUnannotated
public class PortalRespVO {
@Schema(description = "门户编号", example = "1024")
@ExcelProperty("门户编号")
private Long id;
@Schema(description = "门户名称", example = "采购管理系统")
@ExcelProperty("门户名称")
private String name;
@Schema(description = "门户描述", example = "统一采购平台,支持在线询价、比价")
@ExcelProperty("门户描述")
private String description;
@Schema(description = "门户地址", example = "http://purchase.zt.com")
@ExcelProperty("门户地址")
private String url;
@Schema(description = "门户图标Element Plus图标", example = "ep:shopping-cart")
private String icon;
@Schema(description = "图标类型", example = "1")
@ExcelProperty("图标类型")
private Integer iconType;
@Schema(description = "图标图片URL当 iconType=2 时使用)", example = "https://example.com/icon.png")
private String iconUrl;
@Schema(description = "门户分类", example = "业务系统")
@ExcelProperty("门户分类")
private String category;
@Schema(description = "显示排序", example = "1")
@ExcelProperty("显示排序")
private Integer sort;
@Schema(description = "状态", example = "0")
@ExcelProperty(value = "状态", converter = com.zt.plat.framework.excel.core.convert.DictConvert.class)
private Integer status;
@Schema(description = "权限标识", example = "portal:purchase:access")
@ExcelProperty("权限标识")
private String permission;
@Schema(description = "打开方式", example = "1")
@ExcelProperty("打开方式")
private Integer openType;
@Schema(description = "认证配置JSON格式", example = "{\"enabled\":true,\"type\":\"url_params\",\"urlParams\":[{\"key\":\"token\",\"value\":\"${userToken}\"}]}")
private String authConfig;
@Schema(description = "创建时间", example = "2025-11-10 10:00:00")
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,81 @@
package com.zt.plat.module.system.controller.admin.portal.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.*;
/**
* 门户网站创建/修改 Request VO
*
* @author 中铜数字供应链平台
*/
@Schema(description = "管理后台 - 门户网站创建/修改 Request VO")
@Data
public class PortalSaveReqVO {
@Schema(description = "门户编号", example = "1024")
private Long id;
@Schema(description = "门户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "采购管理系统")
@NotEmpty(message = "门户名称不能为空")
@Size(max = 50, message = "门户名称长度不能超过50个字符")
private String name;
@Schema(description = "门户描述", example = "统一采购平台,支持在线询价、比价")
@Size(max = 200, message = "门户描述长度不能超过200个字符")
private String description;
@Schema(description = "门户地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://purchase.zt.com")
@NotEmpty(message = "门户地址不能为空")
@Size(max = 500, message = "门户地址长度不能超过500个字符")
@Pattern(regexp = "^(http|https)://.*", message = "门户地址必须以 http:// 或 https:// 开头")
private String url;
@Schema(description = "门户图标Element Plus图标", example = "ep:shopping-cart")
@Size(max = 100, message = "门户图标长度不能超过100个字符")
private String icon;
@Schema(description = "图标类型", example = "1")
@Min(value = 1, message = "图标类型值不正确")
@Max(value = 2, message = "图标类型值不正确")
private Integer iconType;
@Schema(description = "图标图片URL当 iconType=2 时使用)", example = "https://example.com/icon.png")
@Size(max = 500, message = "图标图片URL长度不能超过500个字符")
private String iconUrl;
@Schema(description = "门户分类", example = "业务系统")
@Size(max = 50, message = "门户分类长度不能超过50个字符")
private String category;
@Schema(description = "显示排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "显示排序不能为空")
@Min(value = 0, message = "显示排序不能小于0")
private Integer sort;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
@NotNull(message = "状态不能为空")
@Min(value = 0, message = "状态值不正确")
@Max(value = 1, message = "状态值不正确")
private Integer status;
@Schema(description = "权限标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "portal:purchase:access")
@NotEmpty(message = "权限标识不能为空")
@Size(max = 100, message = "权限标识长度不能超过100个字符")
@Pattern(regexp = "^portal:[a-z]+:access$", message = "权限标识格式不正确,应为 portal:{module}:access")
private String permission;
@Schema(description = "打开方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "打开方式不能为空")
@Min(value = 1, message = "打开方式值不正确")
@Max(value = 3, message = "打开方式值不正确")
private Integer openType;
@Schema(description = "父菜单ID权限自动同步到该菜单下", example = "2500")
private Long parentMenuId;
@Schema(description = "认证配置JSON格式", example = "{\"enabled\":true,\"type\":\"url_params\",\"urlParams\":[{\"key\":\"token\",\"value\":\"${userToken}\"}]}")
private String authConfig;
}

View File

@@ -0,0 +1,48 @@
package com.zt.plat.module.system.controller.app.portal;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.system.controller.admin.portal.vo.PortalRespVO;
import com.zt.plat.module.system.dal.dataobject.portal.PortalDO;
import com.zt.plat.module.system.service.portal.PortalService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
/**
* 用户端 - 门户网站 Controller
*
* @author 中铜数字供应链平台
*/
@Tag(name = "用户端 - 门户网站")
@RestController
@RequestMapping("/system/portal")
@Validated
public class AppPortalController {
@Resource
private PortalService portalService;
/**
* 获取当前用户可访问的门户列表
* 此接口无需权限验证,因为已经通过登录验证,
* 返回的门户列表已经根据用户权限进行了过滤
*/
@GetMapping("/list")
@Operation(summary = "获取我的门户列表")
public CommonResult<List<PortalRespVO>> getMyPortalList() {
Long userId = getLoginUserId();
List<PortalDO> portals = portalService.getPortalListByUserId(userId);
return success(BeanUtils.toBean(portals, PortalRespVO.class));
}
}

View File

@@ -0,0 +1,106 @@
package com.zt.plat.module.system.dal.dataobject.portal;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.zt.plat.framework.tenant.core.db.TenantBaseDO;
import lombok.*;
/**
* 门户网站 DO
*
* @author 中铜数字供应链平台
*/
@TableName("system_portal")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PortalDO extends TenantBaseDO {
/**
* 门户编号
*/
@TableId
private Long id;
/**
* 门户名称
*/
private String name;
/**
* 门户描述
*/
private String description;
/**
* 门户地址
*/
private String url;
/**
* 门户图标Element Plus图标ep:shopping-cart
*/
private String icon;
/**
* 图标类型
*
* 1 - 图标库(使用 icon 字段)
* 2 - 自定义图片(使用 iconUrl 字段)
*/
private Integer iconType;
/**
* 图标图片URL当 iconType=2 时使用)
*/
private String iconUrl;
/**
* 门户分类
*
* 枚举 {@link PortalCategoryEnum}
*/
private String category;
/**
* 显示排序
*/
private Integer sort;
/**
* 状态
*
* 枚举 {@link com.zt.plat.framework.common.enums.CommonStatusEnum}
*/
private Integer status;
/**
* 权限标识
*
* 格式portal:{module}:access
* 例如portal:purchase:access
*/
private String permission;
/**
* 打开方式
*
* 枚举 {@link PortalOpenTypeEnum}
* 1 - 新窗口打开
* 2 - 当前窗口打开
* 3 - iframe 内嵌
*/
private Integer openType;
/**
* 认证配置JSON格式
*
* 用于配置跳转时的认证信息URL参数或请求头
* 示例:{"enabled":true,"type":"url_params","urlParams":[{"key":"token","value":"${userToken}"}]}
*/
private String authConfig;
}

View File

@@ -0,0 +1,44 @@
package com.zt.plat.module.system.dal.mysql.portal;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.zt.plat.module.system.controller.admin.portal.vo.PortalPageReqVO;
import com.zt.plat.module.system.dal.dataobject.portal.PortalDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 门户网站 Mapper
*
* @author 中铜数字供应链平台
*/
@Mapper
public interface PortalMapper extends BaseMapperX<PortalDO> {
default PageResult<PortalDO> selectPage(PortalPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<PortalDO>()
.likeIfPresent(PortalDO::getName, reqVO.getName())
.eqIfPresent(PortalDO::getStatus, reqVO.getStatus())
.eqIfPresent(PortalDO::getCategory, reqVO.getCategory())
.betweenIfPresent(PortalDO::getCreateTime, reqVO.getCreateTime())
.orderByAsc(PortalDO::getSort)
.orderByDesc(PortalDO::getId));
}
/**
* 根据权限标识列表查询门户列表
*
* @param permissions 权限标识列表
* @return 门户列表
*/
default List<PortalDO> selectListByPermissions(List<String> permissions) {
return selectList(new LambdaQueryWrapperX<PortalDO>()
.in(PortalDO::getPermission, permissions)
.eq(PortalDO::getStatus, 0) // 只查询启用的门户
.orderByAsc(PortalDO::getSort)
.orderByDesc(PortalDO::getId));
}
}

View File

@@ -0,0 +1,29 @@
package com.zt.plat.module.system.enums.portal;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 门户分类枚举
*
* @author 中铜数字供应链平台
*/
@Getter
@AllArgsConstructor
public enum PortalCategoryEnum {
BUSINESS("业务系统", "核心业务系统,如采购、销售、财务等"),
MANAGEMENT("管理工具", "管理辅助工具如OA、数据分析等"),
THIRD_PARTY("第三方系统", "外部集成系统如ERP、E办等");
/**
* 分类名称
*/
private final String name;
/**
* 分类描述
*/
private final String description;
}

View File

@@ -0,0 +1,34 @@
package com.zt.plat.module.system.enums.portal;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 门户打开方式枚举
*
* @author 中铜数字供应链平台
*/
@Getter
@AllArgsConstructor
public enum PortalOpenTypeEnum {
NEW_WINDOW(1, "新窗口", "在新窗口中打开门户"),
CURRENT_WINDOW(2, "当前窗口", "在当前窗口中打开门户"),
IFRAME(3, "iframe内嵌", "在iframe中内嵌显示门户");
/**
* 类型
*/
private final Integer type;
/**
* 名称
*/
private final String name;
/**
* 描述
*/
private final String description;
}

View File

@@ -0,0 +1,63 @@
package com.zt.plat.module.system.service.portal;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.module.system.controller.admin.portal.vo.PortalPageReqVO;
import com.zt.plat.module.system.controller.admin.portal.vo.PortalSaveReqVO;
import com.zt.plat.module.system.dal.dataobject.portal.PortalDO;
import java.util.List;
/**
* 门户网站 Service 接口
*
* @author 中铜数字供应链平台
*/
public interface PortalService {
/**
* 创建门户
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createPortal(PortalSaveReqVO createReqVO);
/**
* 更新门户
*
* @param updateReqVO 更新信息
*/
void updatePortal(PortalSaveReqVO updateReqVO);
/**
* 删除门户
*
* @param id 编号
*/
void deletePortal(Long id);
/**
* 获得门户
*
* @param id 编号
* @return 门户
*/
PortalDO getPortal(Long id);
/**
* 获得门户分页
*
* @param pageReqVO 分页查询
* @return 门户分页
*/
PageResult<PortalDO> getPortalPage(PortalPageReqVO pageReqVO);
/**
* 获得用户有权限访问的门户列表
*
* @param userId 用户ID
* @return 门户列表
*/
List<PortalDO> getPortalListByUserId(Long userId);
}

View File

@@ -0,0 +1,193 @@
package com.zt.plat.module.system.service.portal;
import com.google.common.annotations.VisibleForTesting;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.module.system.controller.admin.permission.vo.menu.MenuSaveVO;
import com.zt.plat.module.system.controller.admin.portal.vo.PortalPageReqVO;
import com.zt.plat.module.system.controller.admin.portal.vo.PortalSaveReqVO;
import com.zt.plat.module.system.dal.dataobject.permission.MenuDO;
import com.zt.plat.module.system.dal.dataobject.portal.PortalDO;
import com.zt.plat.module.system.dal.mysql.portal.PortalMapper;
import com.zt.plat.module.system.enums.permission.MenuTypeEnum;
import com.zt.plat.module.system.service.permission.MenuService;
import com.zt.plat.module.system.service.permission.PermissionService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.zt.plat.module.system.enums.ErrorCodeConstants.PORTAL_NOT_EXISTS;
/**
* 门户网站 Service 实现类
*
* @author 中铜数字供应链平台
*/
@Service
public class PortalServiceImpl implements PortalService {
@Resource
private PortalMapper portalMapper;
@Resource
private PermissionService permissionService;
@Resource
private MenuService menuService;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createPortal(PortalSaveReqVO createReqVO) {
// 1. 创建门户
PortalDO portal = BeanUtils.toBean(createReqVO, PortalDO.class);
portalMapper.insert(portal);
// 2. 自动创建对应的菜单权限
if (createReqVO.getParentMenuId() != null) {
syncMenuPermission(portal, createReqVO.getParentMenuId(), null);
}
return portal.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updatePortal(PortalSaveReqVO updateReqVO) {
// 1. 校验是否存在
PortalDO oldPortal = validatePortalExists(updateReqVO.getId());
// 2. 更新门户
PortalDO updateObj = BeanUtils.toBean(updateReqVO, PortalDO.class);
portalMapper.updateById(updateObj);
// 3. 自动更新对应的菜单权限
if (updateReqVO.getParentMenuId() != null) {
syncMenuPermission(updateObj, updateReqVO.getParentMenuId(), oldPortal.getPermission());
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deletePortal(Long id) {
// 1. 校验是否存在
PortalDO portal = validatePortalExists(id);
// 2. 删除门户
portalMapper.deleteById(id);
// 3. 自动删除对应的菜单权限
deleteMenuPermission(portal.getPermission());
}
@Override
public PortalDO getPortal(Long id) {
return portalMapper.selectById(id);
}
@Override
public PageResult<PortalDO> getPortalPage(PortalPageReqVO pageReqVO) {
return portalMapper.selectPage(pageReqVO);
}
@Override
public List<PortalDO> getPortalListByUserId(Long userId) {
// 1. 获取用户的角色ID列表
Set<Long> roleIds = permissionService.getUserRoleIdListByUserIdFromCache(userId);
if (roleIds.isEmpty()) {
return Collections.emptyList();
}
// 2. 获取角色对应的菜单ID列表
Set<Long> menuIds = permissionService.getRoleMenuListByRoleId(roleIds);
if (menuIds.isEmpty()) {
return Collections.emptyList();
}
// 3. 获取菜单列表并提取权限标识
List<MenuDO> menus = menuService.getMenuList(menuIds);
List<String> permissions = menus.stream()
.map(MenuDO::getPermission)
.filter(permission -> permission != null && !permission.isEmpty())
.collect(Collectors.toList());
if (permissions.isEmpty()) {
return Collections.emptyList();
}
// 4. 根据权限标识查询门户列表
return portalMapper.selectListByPermissions(permissions);
}
@VisibleForTesting
public PortalDO validatePortalExists(Long id) {
if (id == null) {
return null;
}
PortalDO portal = portalMapper.selectById(id);
if (portal == null) {
throw exception(PORTAL_NOT_EXISTS);
}
return portal;
}
/**
* 同步菜单权限
* - 如果菜单权限不存在,则创建
* - 如果菜单权限已存在,则更新
*
* @param portal 门户对象
* @param parentMenuId 父菜单 ID
* @param oldPermission 旧的权限标识(用于更新场景)
*/
private void syncMenuPermission(PortalDO portal, Long parentMenuId, String oldPermission) {
// 1. 如果权限标识发生变化,先删除旧的菜单权限
if (oldPermission != null && !oldPermission.equals(portal.getPermission())) {
deleteMenuPermission(oldPermission);
}
// 2. 查询是否已存在该权限的菜单
List<Long> existMenuIds = menuService.getMenuIdListByPermissionFromCache(portal.getPermission());
// 3. 构建菜单对象
MenuSaveVO menuVO = new MenuSaveVO();
menuVO.setName("访问" + portal.getName());
menuVO.setPermission(portal.getPermission());
menuVO.setType(MenuTypeEnum.BUTTON.getType());
menuVO.setSort(portal.getSort());
menuVO.setParentId(parentMenuId);
menuVO.setStatus(portal.getStatus());
menuVO.setVisible(false); // 按钮权限不显示在菜单中
// 4. 如果菜单已存在则更新,否则创建
if (!CollectionUtils.isEmpty(existMenuIds)) {
menuVO.setId(existMenuIds.get(0));
menuService.updateMenu(menuVO);
} else {
menuService.createMenu(menuVO);
}
}
/**
* 删除菜单权限
*
* @param permission 权限标识
*/
private void deleteMenuPermission(String permission) {
if (permission == null || permission.isEmpty()) {
return;
}
List<Long> menuIds = menuService.getMenuIdListByPermissionFromCache(permission);
if (!CollectionUtils.isEmpty(menuIds)) {
menuIds.forEach(menuService::deleteMenu);
}
}
}