diff --git a/README.md b/README.md index 4b924ca9..c53b1d9f 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ dev环境启动参数: -Dspring.datasource.url=jdbc:mysql://172.16.2.197:3311/pudge-dev?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&serverTimezone=Asia/Shanghai&useSSL=true&verifyServerCertificate=false&rewriteBatchedStatements=true --Drocketmq.name-server=114.116.202.128:9876 +-Drocketmq.name-server=172.16.2.82:9876 -DCUSTOM_ENV=dev -Dspring.profiles.active=dev --Dspring.redis.host=123.249.44.111 +-Dspring.redis.host=172.16.2.23 -Dspring.redis.port=31270 -Dserver.port=8080 \ No newline at end of file diff --git a/tyr-api/pom.xml b/tyr-api/pom.xml index 08786447..56cc7297 100644 --- a/tyr-api/pom.xml +++ b/tyr-api/pom.xml @@ -26,5 +26,12 @@ cn.axzo.basics basics-common + + + cn.axzo.foundation + dao-support-lib + 2.0.0-SNAPSHOT + + diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/AttrPermissionEnum.java b/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/AttrPermissionEnum.java index c4bd8666..3144b4ba 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/AttrPermissionEnum.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/AttrPermissionEnum.java @@ -22,7 +22,7 @@ public enum AttrPermissionEnum { UNIT_COOPERATE_SUBORDINATE(7, "本单位及下级协同(直属+合作)单位数据"), UNIT_ALL_SUBORDINATE(8, "本单位及以下协同(直属+合作)单位数据"), - WORKSPACE(9, "本项目部数据"), + WORKSPACE(9, "本项目数据"), EQUAL_TO_ROW(10, "同行级数据权限"), UNIT_ONLY_EXCLUDE_TEAM(11, "仅本单位数据(不包含班组)"), diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/FeatureResourceType.java b/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/FeatureResourceType.java index bdecbbf9..b7d0673d 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/FeatureResourceType.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/FeatureResourceType.java @@ -23,6 +23,10 @@ public enum FeatureResourceType { PAGE(2, "页面"), APP_ENTRY(3, "应用入口"), COMPONENT(4, "组件"), + ROOT(5, "ROOT"), + GROUP(6, "分组"), + // 用户pc端菜单直接的分割分组线 + MENU_PARTITION_GROUP(7, "菜单分割分组"), ; private static final Map MAPPING = new HashMap<>(); @@ -50,7 +54,9 @@ public enum FeatureResourceType { public static List navTypes() { return Arrays.asList(FeatureResourceType.MENU.getCode(), FeatureResourceType.PAGE.getCode(), - FeatureResourceType.APP_ENTRY.getCode()); + FeatureResourceType.APP_ENTRY.getCode(), + FeatureResourceType.ROOT.getCode(), + FeatureResourceType.GROUP.getCode()); } public static List pageTypes() { diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/PageElementFeatureResourceRelationTypeEnum.java b/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/PageElementFeatureResourceRelationTypeEnum.java new file mode 100644 index 00000000..1105d9f6 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/PageElementFeatureResourceRelationTypeEnum.java @@ -0,0 +1,28 @@ +package cn.axzo.tyr.client.common.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author likunpeng + * @date 2024/7/2 + */ +@Getter +@AllArgsConstructor +public enum PageElementFeatureResourceRelationTypeEnum { + /** + * 默认类型 + */ + DEFAULT(0, "默认类型"), + + /** + * 页面路由 + */ + PAGE_ROUTE(1, "页面路由"), + ; + + @EnumValue + private final Integer value; + private final String desc; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/PageElementTypeEnum.java b/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/PageElementTypeEnum.java new file mode 100644 index 00000000..d51b9070 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/PageElementTypeEnum.java @@ -0,0 +1,23 @@ +package cn.axzo.tyr.client.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/6/18 + */ +@Getter +@AllArgsConstructor +public enum PageElementTypeEnum { + + PAGE("PAGE", "页面"), + COMPONENT("COMPONENT", "组件"), + + ; + + private final String code; + + private final String desc; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/PermissionRelationOperateLogSceneEnum.java b/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/PermissionRelationOperateLogSceneEnum.java new file mode 100644 index 00000000..cc3c4adf --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/PermissionRelationOperateLogSceneEnum.java @@ -0,0 +1,101 @@ +package cn.axzo.tyr.client.common.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/7/24 + */ +@Getter +@AllArgsConstructor +public enum PermissionRelationOperateLogSceneEnum { + + /** + * oms同步 + */ + OMS_SYNC("OMS_SYNC", "oms同步"), + + /** + * oms后台更新资源绑定的角色 + */ + OMS_FEATURE_RESOURCE_BIND_ROLE("OMS_FEATURE_RESOURCE_BIND_ROLE", "oms后台更新资源绑定的角色"), + + /** + * oms后台更新角色绑定资源绑定的 + */ + OMS_ROLE_BIND_FEATURE_RESOURCE("OMS_ROLE_BIND_FEATURE_RESOURCE", "oms后台更新角色绑定的资源"), + + /** + * [老]oms后台更新角色绑定资源绑定的 + */ + OLD_OMS_ROLE_BIND_FEATURE("OLD_OMS_ROLE_BIND_FEATURE", "[老]oms后台更新角色绑定的资源"), + + /** + * oms后台更新资源绑定的页面元素 + */ + OMS_FEATURE_RESOURCE_BIND_ELEMENT("OMS_FEATURE_RESOURCE_BIND_ELEMENT", "oms后台更新资源绑定的页面元素"), + + /** + * oms后台更新产品榜的资源 + */ + OMS_PRODUCT_BIND_FEATURE_RESOURCE("OMS_PRODUCT_BIND_FEATURE_RESOURCE", "oms后台更新产品榜的资源"), + + /** + * [老]oms后台更新产品权限点 + */ + OLD_OMS_PRODUCT__UPDATE("OLD_OMS_PRODUCT__UPDATE", "[老]oms后台更新产品"), + + /** + * [老]oms后台更新产品权限点 + */ + OLD_OMS_PRODUCT_PERMISSION_POINT__SAVE_BATCH("OLD_OMS_PRODUCT_PERMISSION_POINT__SAVE_BATCH", "[老]oms后台更新产品权限点"), + + /** + * [老]角色分组新增/更新 + */ + OLD_OMS_ROLE_GROUP__SAVE_OR_UPDATE("OLD_OMS_ROLE_GROUP__SAVE_OR_UPDATE", "[老]角色分组新增/更新"), + + /** + * [老]角色分组删除 + */ + OLD_OMS_ROLE_GROUP__DELETE("OLD_OMS_ROLE_GROUP__DELETE", "[老]角色分组删除"), + + /** + * [老]权限点删除 + */ + OLD_OMS_SAAS_FEATURE__DELETE("OLD_OMS_SAAS_FEATURE__DELETE", "[老]权限点删除"), + + /** + * [老]权限点新增/更新 + */ + OLD_OMS_SAAS_FEATURE__SAVE_OR_UPDATE("OLD_OMS_SAAS_FEATURE__SAVE_OR_UPDATE", "[老]权限点新增/更新"), + + /** + * oms后台添加API + */ + OMS_API_INSERT("OMS_API_INSERT", "oms后台添加API"), + + /** + * oms后台更新API + */ + OMS_API_UPDATE("OMS_API_UPDATE", "oms后台更新API"), + + /** + * oms后台删除API + */ + OMS_API_DELETE("OMS_API_DELETE", "oms后台删除API"), + + /** + * oms后台同步API + */ + OMS_API_SYNC("OMS_API_SYNC", "oms后台同步API"), + + ; + + @EnumValue + private final String value; + private final String desc; +} \ No newline at end of file diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/ProductModuleFeatureRelationTypeEnum.java b/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/ProductModuleFeatureRelationTypeEnum.java new file mode 100644 index 00000000..80efd4fb --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/ProductModuleFeatureRelationTypeEnum.java @@ -0,0 +1,27 @@ +package cn.axzo.tyr.client.common.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/5/13 + */ +public enum ProductModuleFeatureRelationTypeEnum { + + FEATURE(0, "FEATURE"), + FEATURE_RESOURCE(1, "FEATURE_RESOURCE") + ; + @Getter + @EnumValue + @JsonValue + public final Integer code; + public final String desc; + + ProductModuleFeatureRelationTypeEnum(Integer code, String desc){ + this.code = code; + this.desc = desc; + } +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/RoleTypeEnum.java b/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/RoleTypeEnum.java index 04a09e7f..c94a57f7 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/RoleTypeEnum.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/common/enums/RoleTypeEnum.java @@ -20,12 +20,12 @@ import java.util.stream.Collectors; @AllArgsConstructor public enum RoleTypeEnum { - //角色类型:common 普通角色 super_admin超级管理员(禁止删除) admin子管理员(禁止删除) init初始化内置角色 auto_own自定义角色(禁止删除)<承载向用户单独分配的自定义权限> - COMMON("common", "普通角色",false), + //角色类型:common 自定义角色 super_admin超级管理员(禁止删除) admin子管理员(禁止删除) init初始化内置角色 auto_own自定义角色(禁止删除)<承载向用户单独分配的自定义权限> + COMMON("common", "自定义角色",false), SUPER_ADMIN("super_admin", "超级管理员",true), ADMIN("admin", "子管理员",true), - INIT("init", "初始化内置角色",false), - AUTO_OWN("auto_own", "自定义角色",false); + INIT("init", "初始化内置角色-标准角色",false), + AUTO_OWN("auto_own", "虚拟角色(自定义权限使用)",false); @EnumValue private final String value; diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/FeatureResourceApi.java b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/FeatureResourceApi.java index ef3833f4..e7867591 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/FeatureResourceApi.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/FeatureResourceApi.java @@ -1,12 +1,13 @@ package cn.axzo.tyr.client.feign; import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.tyr.client.model.req.DeleteFeatureResourceReq; +import cn.axzo.tyr.client.model.req.FeatureResourceTreeSaveReq; import cn.axzo.tyr.client.model.req.GetFeatureResourceTreeReq; import cn.axzo.tyr.client.model.req.ResourceSyncReq; -import cn.axzo.tyr.client.model.req.FeatureResourceTreeSaveReq; -import cn.axzo.tyr.client.model.res.FeatureResourceDetailResp; import cn.axzo.tyr.client.model.res.FeatureResourceTreeNode; import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -14,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestParam; import javax.validation.Valid; import java.util.List; +import java.util.Map; /** * 功能资源API接口 @@ -44,7 +46,7 @@ public interface FeatureResourceApi { /** 删除菜单/页面/组件 **/ @PostMapping("/api/featureResource/delete") - ApiResult deleteFeatureResource(@RequestParam Long featureId, @RequestParam Long operatorId); + ApiResult deleteFeatureResource(@Validated @RequestBody DeleteFeatureResourceReq req); /** 重排序菜单/页面/组件 **/ @@ -58,4 +60,8 @@ public interface FeatureResourceApi { /** 从基准环境查询功能资源树 **/ @PostMapping("/api/featureResource/getBaseTree") ApiResult> getBaseTree(@RequestBody @Valid GetFeatureResourceTreeReq req); + + /** 通过featureResourceIds查询对应的featureCode **/ + @PostMapping("/api/featureResource/listFeatureCodeByFeatureResourceIds") + ApiResult> listFeatureCodeByFeatureResourceIds(@RequestParam List featureResourceIds); } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/PageElementApi.java b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/PageElementApi.java new file mode 100644 index 00000000..76373ffd --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/PageElementApi.java @@ -0,0 +1,44 @@ +package cn.axzo.tyr.client.feign; + +import cn.axzo.framework.domain.web.result.ApiPageResult; +import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.tyr.client.model.req.*; +import cn.axzo.tyr.client.model.res.GetUserHasPermissionPageElementResp; +import cn.axzo.tyr.client.model.res.PageElementResp; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import javax.validation.Valid; +import java.util.List; + +/** + * 页面及元素相关接口 + * + * @author likunpeng + * @version 1.0 + * @date 2024/6/18 + */ +@FeignClient(name = "tyr", url = "${axzo.service.tyr:http://tyr:8080}") +public interface PageElementApi { + + /** 页面及元素上报 **/ + @PostMapping("/api/pageElement/report") + ApiResult report(@RequestBody @Valid PageElementReportReq req); + + /** 查询页面资源 **/ + @PostMapping("/api/pageElement/getPageElement") + ApiResult> getPageElement(@RequestBody @Valid GetPageElementReq req); + + /** 编辑页面、页面组件的元素 **/ + @PostMapping("/api/pageElement/modifyPageElementRelation") + ApiResult modifyPageElementRelation(@RequestBody @Valid ModifyPageElementRelationDTO req); + + /** 分页查询页面资源 **/ + @PostMapping("/api/pageElement/page") + ApiPageResult page(@RequestBody @Valid PageQueryElementReq req); + + /** 根据用户传入的页面code,查询用户有权限的元素code **/ + @PostMapping("/api/pageElement/getUserHasPermissionPageElement") + ApiResult getUserHasPermissionPageElement(@RequestBody @Valid GetUserHasPermissionPageElementReq req); +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/PermissionOperateLogApi.java b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/PermissionOperateLogApi.java new file mode 100644 index 00000000..418a9120 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/PermissionOperateLogApi.java @@ -0,0 +1,22 @@ +package cn.axzo.tyr.client.feign; + +import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.tyr.client.model.req.PermissionOperateLogReq; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import javax.validation.Valid; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/7/25 + */ +@FeignClient(name = "tyr", url = "${axzo.service.tyr:http://tyr:8080}") +public interface PermissionOperateLogApi { + + /** API关联操作日志记录 **/ + @PostMapping("/api/permissionOperateLog/save") + ApiResult save(@RequestBody @Valid PermissionOperateLogReq req); +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/PermissionPointApi.java b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/PermissionPointApi.java index 3e5e6cc3..e2b5a5be 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/PermissionPointApi.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/PermissionPointApi.java @@ -1,6 +1,7 @@ package cn.axzo.tyr.client.feign; import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.tyr.client.model.permission.DeletePermissionPointRequest; import cn.axzo.tyr.client.model.permission.PermissionPointDTO; import cn.axzo.tyr.client.model.permission.PermissionPointListQueryRequest; import cn.axzo.tyr.client.model.permission.PermissionPointMoveRequest; @@ -16,6 +17,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; +import javax.validation.Valid; import java.util.List; /** @@ -48,6 +50,10 @@ public interface PermissionPointApi { @PostMapping(value = "/api/v1/permissionPoint/delete/{permissionId}") ApiResult> deletePermissionPoint(@PathVariable Long permissionId); + /** 删除权限点 **/ + @PostMapping(value = "/api/v1/permissionPoint/delete/v2/deletePermissionPoint") + ApiResult> deletePermissionPointV2(@Valid @RequestBody DeletePermissionPointRequest request); + /** 位置移动 **/ @PostMapping(value = "/api/v1/permissionPoint/move") diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/PermissionQueryApi.java b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/PermissionQueryApi.java index dc6962c2..3c77b8ff 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/PermissionQueryApi.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/PermissionQueryApi.java @@ -5,8 +5,14 @@ import cn.axzo.tyr.client.model.req.NavTreeReq; import cn.axzo.tyr.client.model.req.PagePermissionReq; import cn.axzo.tyr.client.model.req.PagePermissionResp; import cn.axzo.tyr.client.model.req.PermissionCheckReq; +import cn.axzo.tyr.client.model.req.TreePermissionReq; +import cn.axzo.tyr.client.model.req.TreeProductFeatureResourceReq; +import cn.axzo.tyr.client.model.res.FeatureResourceDTO; import cn.axzo.tyr.client.model.res.NavTreeResp; +import cn.axzo.tyr.client.model.res.ProductFeatureResourceResp; +import cn.axzo.tyr.client.model.res.TreePermissionResp; import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -25,6 +31,7 @@ public interface PermissionQueryApi { /** 返回有权限的导航菜单页面 **/ @PostMapping(value = "/api/v3/permission/query/getNavTree") + @Deprecated ApiResult> getNavTree(@RequestBody @Valid NavTreeReq req); /** 页面权限详情:页面自身及所有下级 **/ @@ -34,4 +41,30 @@ public interface PermissionQueryApi { /** 鉴权接口 **/ @PostMapping(value = "/api/v3/permission/query/hasPermission") ApiResult hasPermission(@RequestBody @Valid PermissionCheckReq req); + + /** + * 查询租户的权限树 + * @param request + * @return + */ + @PostMapping(value = "/api/v3/productPermission/tree") + ApiResult> treeProduct(@RequestBody @Validated TreeProductFeatureResourceReq request); + + /** + * 查询租户的权限点list + * @param request + * @return + */ + @PostMapping(value = "/api/v3/productPermission/list") + ApiResult> listFeatureResource(@RequestBody @Validated TreeProductFeatureResourceReq request); + + /** + * 查询用户有权限的菜单资源树 + * getNavTree已经废弃,不在原方法上改的原因:OMS端现在没有保存角色的菜单权限,且不通用,不能传入指定类型的节点和树种某个节点下的子节点 + * 待oms端把菜单的权限点保存且使用前端的页面元素后就可以切换这个接口 + * @param req + * @return + */ + @PostMapping(value = "/api/v3/permission/featureResource/tree") + ApiResult> treePermission(@RequestBody @Validated TreePermissionReq req); } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/ProductApi.java b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/ProductApi.java index c042e336..e32682ea 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/ProductApi.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/ProductApi.java @@ -2,7 +2,9 @@ package cn.axzo.tyr.client.feign; import cn.axzo.framework.domain.web.result.ApiPageResult; import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.tyr.client.model.product.OldUpdateFeatureRelationRequestV2; import cn.axzo.tyr.client.model.product.ProductAddReq; +import cn.axzo.tyr.client.model.product.ProductDetailReq; import cn.axzo.tyr.client.model.product.ProductFeatureRelationSearchReq; import cn.axzo.tyr.client.model.product.ProductFeatureRelationUpdateReq; import cn.axzo.tyr.client.model.product.ProductFeatureRelationVO; @@ -10,6 +12,10 @@ import cn.axzo.tyr.client.model.product.ProductSearchListReq; import cn.axzo.tyr.client.model.product.ProductSearchPageReq; import cn.axzo.tyr.client.model.product.ProductUpdateReq; import cn.axzo.tyr.client.model.product.ProductVO; +import cn.axzo.tyr.client.model.req.ProductSaveReq; +import cn.axzo.tyr.client.model.req.UpdateProductStatusReq; +import cn.axzo.tyr.client.model.res.GovernmentTerminalResp; +import cn.axzo.tyr.client.model.res.WorkspaceProductResp; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; @@ -105,6 +111,16 @@ public interface ProductApi { @PostMapping("api/auth/product/feature/relation/update") ApiResult updateFeatureRelation(@Validated @RequestBody List req); + /** + * 更新产品与权限点的关联关系 + *

携带操作人员信息 + * + * @param req {@link ProductFeatureRelationUpdateReq} + * @return + */ + @PostMapping("api/auth/product/feature/relation/v2/update") + ApiResult updateFeatureRelationV2(@Validated @RequestBody OldUpdateFeatureRelationRequestV2 req); + /** * 查询指定工作台关联的服务包下的产品权限点 * @@ -113,4 +129,48 @@ public interface ProductApi { */ @PostMapping("api/auth/product/feature/query") ApiResult>> queryProductFeatureRelationByWorkspace(@RequestBody Set workspaceIds); + + + /** + * 产品上下架 + * + * @param req 产品id及状态 + * @return 是否更新成功 + */ + @PostMapping("api/auth/product/updateStatus") + ApiResult updateStatus(@Validated @RequestBody UpdateProductStatusReq req); + + + /** + * 保存、更新产品(v2版本) + * + * @param req 产品信息 + * @return 返回产品ID + */ + @PostMapping("api/auth/product/saveOrUpdate") + ApiResult saveOrUpdate(@Validated @RequestBody ProductSaveReq req); + + /** + * 获取政务端列表 + * + * @param terminal 政务端 + * @return {@link GovernmentTerminalResp} + */ + @GetMapping("api/auth/product/getGovernmentTerminal") + ApiResult> getGovernmentTerminal(@RequestParam @NotNull(message = "terminal不能为空") String terminal); + + /** + * 根据租户类型查询产品信息(新增租户、开通管理) + * + * @param workspaceType 租户类型 + * @return 租户产品信息 + */ + @GetMapping("api/auth/product/getWorkspaceProduct") + ApiResult getWorkspaceProduct(@RequestParam @NotNull(message = "workspaceType不能为空") String workspaceType); + + /** + * 获取指定ID的产品详细信息 + */ + @PostMapping("api/auth/product/getDetail") + ApiResult getDetail(@Validated @RequestBody ProductDetailReq req); } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/SaasRoleGroupApi.java b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/SaasRoleGroupApi.java index 0df9e9c7..c8f029f9 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/SaasRoleGroupApi.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/SaasRoleGroupApi.java @@ -1,8 +1,13 @@ package cn.axzo.tyr.client.feign; +import cn.axzo.foundation.page.PageResp; import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.tyr.client.model.req.ListSaasRoleGroupParam; +import cn.axzo.tyr.client.model.req.PageSaasRoleGroupParam; import cn.axzo.tyr.client.model.req.QuerySaasRoleGroupReq; +import cn.axzo.tyr.client.model.req.SaasRoleGroupDeleteRequest; import cn.axzo.tyr.client.model.req.UpdateRoleGroupOffsetReq; +import cn.axzo.tyr.client.model.res.SaasRoleGroupDTO; import cn.axzo.tyr.client.model.vo.SaasRoleGroupVO; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; @@ -53,6 +58,14 @@ public interface SaasRoleGroupApi { @PostMapping("/api/saasRoleGroup/delete") ApiResult delete(@RequestParam @NotEmpty List ids); + /** + * 删除角色分组 + * @param request 角色分组ID + * @return 返回删除角色分组状态 + */ + @PostMapping("/api/saasRoleGroup/v2/delete") + ApiResult deleteV2(@RequestBody @Valid SaasRoleGroupDeleteRequest request); + /** * * 通过categoryCode查询分组 @@ -72,4 +85,20 @@ public interface SaasRoleGroupApi { */ @PostMapping("/api/role/group/offset/update") ApiResult updateRoleGroupOffset(@Valid @RequestBody UpdateRoleGroupOffsetReq request); + + /** + * 角色分组page接口 + * @param request + * @return + */ + @PostMapping("/api/saasRoleGroup/page") + ApiResult> page(@RequestBody PageSaasRoleGroupParam request); + + /** + * 角色分组list接口 + * @param request + * @return + */ + @PostMapping("/api/saasRoleGroup/list") + ApiResult> list(@RequestBody ListSaasRoleGroupParam request); } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/TyrSaasRoleApi.java b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/TyrSaasRoleApi.java index f26b468e..9e351c72 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/TyrSaasRoleApi.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/TyrSaasRoleApi.java @@ -9,6 +9,7 @@ import cn.axzo.tyr.client.model.req.ListRoleReq; import cn.axzo.tyr.client.model.req.PageRoleReq; import cn.axzo.tyr.client.model.req.QueryByIdentityIdTypeReq; import cn.axzo.tyr.client.model.req.QueryRoleByNameReq; +import cn.axzo.tyr.client.model.req.QuerySaasRoleGroupReq; import cn.axzo.tyr.client.model.req.QuerySaasRoleReq; import cn.axzo.tyr.client.model.req.RoleWithUserQueryReq; import cn.axzo.tyr.client.model.req.TreeRoleReq; diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/TyrSaasRoleUserApi.java b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/TyrSaasRoleUserApi.java index da634195..09e90b0d 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/TyrSaasRoleUserApi.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/TyrSaasRoleUserApi.java @@ -2,8 +2,11 @@ package cn.axzo.tyr.client.feign; import cn.axzo.framework.domain.web.result.ApiPageResult; import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.tyr.client.model.roleuser.RoleUserUpdateReq; import cn.axzo.tyr.client.model.roleuser.dto.GetUserAutoOwnRoleResp; +import cn.axzo.tyr.client.model.roleuser.dto.GetUserFeatureResourceIdsResp; import cn.axzo.tyr.client.model.roleuser.dto.SaasRoleUserDTO; +import cn.axzo.tyr.client.model.roleuser.dto.SaasRoleUserV2DTO; import cn.axzo.tyr.client.model.roleuser.dto.SuperAminInfoResp; import cn.axzo.tyr.client.model.roleuser.req.*; import org.springframework.cloud.openfeign.FeignClient; @@ -70,6 +73,12 @@ public interface TyrSaasRoleUserApi { @PostMapping("/api/saas-role-user/list") ApiResult> roleUserList(@RequestBody @Valid RoleUserParam param); + /** + * 用户角色列表 限制500条 + */ + @PostMapping("/api/saas-role-user/list/v2") + ApiResult> roleUserListV2(@RequestBody @Valid ListRoleUserRelationParam param); + /** * 批量查询超管, 只能批量获取单个工作台台下的超管,业务系统需要根据返回的identityId和identityType查询identityProfile查询用户明细信息 */ @@ -106,11 +115,25 @@ public interface TyrSaasRoleUserApi { * 保存/更新 用户自定义权限,每次传入新的featureIds都会覆盖原来的所有featureIds */ @PostMapping("/api/saas-role-user/save-or-update-auto-own-role") - ApiResult saveOrUpdateAutoOwnRole(@RequestBody @Valid AutoOwnRoleUserReq req); + ApiResult saveOrUpdateAutoOwnRole(@RequestBody @Valid AutoOwnRoleUserReq req); /** * 查询用户自定义角色和权限 */ @PostMapping("/api/saas-role-user/get-auto-own-role") - ApiResult getUserAutoOwnRole(@RequestBody @Valid GetUserAutoOwnRoleReq req); + ApiResult getUserAutoOwnRole(@RequestBody @Valid GetUserAutoOwnRoleReq req); + + /** + * 查询用户权限(featureResourceId) + */ + @PostMapping("/api/saas-role-user/get-user-feature-resource-ids") + ApiResult getUserFeatureResourceIds(@RequestBody @Valid GetUserFeatureResourceIdsReq req); + + /** + * 根据id更新数据 + * @param roleUserUpdateReqs 参数列表 + * @return + */ + @PostMapping("/api/saas-role-user/batch-update-by-id") + ApiResult batchUpdateById(@RequestBody @Valid List roleUserUpdateReqs); } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/base/BaseFeatureResourceDO.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/base/BaseFeatureResourceDO.java index aad00da3..63a61833 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/base/BaseFeatureResourceDO.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/base/BaseFeatureResourceDO.java @@ -5,6 +5,8 @@ import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; +import java.util.List; + @Data @SuperBuilder @NoArgsConstructor @@ -21,11 +23,11 @@ public class BaseFeatureResourceDO { /** 资源名称 **/ private String featureName; - /** 资源类型 1-菜单 2-页面 3-应用入口 4-组件 **/ + /** 资源类型 1-菜单 2-页面 3-应用入口 4-组件 5-ROOT根节点 **/ private Integer featureType; /** 资源编码 **/ - private String featureCode; + private String uniCode; /** 状态 0-隐藏 1-显示 **/ private Integer status; @@ -38,4 +40,10 @@ public class BaseFeatureResourceDO { /** 组件类型 **/ private Integer componentType; + + /** 组件元素的code列表 */ + private List pageElementCodes; + + /** 图片资源对象 **/ + private FeatureResourceExtraDO extra; } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/base/FeatureResourceExtraDO.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/base/FeatureResourceExtraDO.java new file mode 100644 index 00000000..5b17780b --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/base/FeatureResourceExtraDO.java @@ -0,0 +1,30 @@ +package cn.axzo.tyr.client.model.base; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/6/24 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FeatureResourceExtraDO implements Serializable { + + /** + * 选中的图标 + */ + private String activeIcon; + + /** + * 更多图标 + */ + private String moreIcon; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/data/object/DefaultDataObjectRuleBO.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/data/object/DefaultDataObjectRuleBO.java index ff59619d..2c44f8e7 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/data/object/DefaultDataObjectRuleBO.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/data/object/DefaultDataObjectRuleBO.java @@ -33,7 +33,7 @@ public class DefaultDataObjectRuleBO { /** * 行级数据权限(单选) 1:仅本人数据 2:本人及下属数据 3:仅本部门数据 4:本部门及以下数据 - * 5:仅本单位数据 6:本单位及下级直属单位数据 7:本单位及下级协同(直属+合作)单位数据 8:本单位及以下协同(直属+合作)单位数据 9:本项目部数据 + * 5:仅本单位数据 6:本单位及下级直属单位数据 7:本单位及下级协同(直属+合作)单位数据 8:本单位及以下协同(直属+合作)单位数据 9:本项目数据 */ private Integer rowPermission; /** diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/enums/DictWorkSpaceTypeEnum.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/enums/DictWorkSpaceTypeEnum.java index 8af050e2..bb34436b 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/enums/DictWorkSpaceTypeEnum.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/enums/DictWorkSpaceTypeEnum.java @@ -23,11 +23,11 @@ public enum DictWorkSpaceTypeEnum { ENT("ent", "企业工作台", 7,1, "单位租户"), /** - * 项目部工作台 + * 项目工作台 */ - PROJ("proj", "项目部工作台",1,2, "项目租户"), + PROJ("proj", "项目工作台",1,2, "项目租户"), - GOVERNMENT("government", "政务监管平台",3,3, "政务监管平台"), + GOVERNMENT("gov", "政务监管平台",3,3, "政务监管平台"), /** * OMS工作台 diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/enums/PermissionScopeType.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/enums/PermissionScopeType.java index d08e51a0..f8f8286d 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/enums/PermissionScopeType.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/enums/PermissionScopeType.java @@ -9,7 +9,7 @@ import java.util.Map; @Getter @AllArgsConstructor public enum PermissionScopeType { - WORKSPACE("workspace", "项目部"), + WORKSPACE("workspace", "项目"), OU ("ou", "单位"), ; diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/enums/ProductModuleCategoryEnum.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/enums/ProductModuleCategoryEnum.java new file mode 100644 index 00000000..ac80ca05 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/enums/ProductModuleCategoryEnum.java @@ -0,0 +1,64 @@ +package cn.axzo.tyr.client.model.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import com.google.common.collect.Lists; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/5/6 + */ +@Getter +@AllArgsConstructor +public enum ProductModuleCategoryEnum { + + /** + * 产品版本 + */ + PRODUCT_VERSION("PRODUCT_VERSION", "产品版本"), + + /** + * 增值服务包 + */ + ADD_VALUE_SERVICE("ADD_VALUE_SERVICE", "增值服务包"), + + /** + * 通用产品 + */ + GENERAL_SERVICE("GENERAL_SERVICE", "通用产品"), + + /** + * 硬件产品 + */ + HARD_WARE("HARD_WARE", "硬件产品"), + + ; + + @EnumValue + @JsonValue + private String code; + private String desc; + + /** + * 通过value值获取枚举类型 + * + * @param code code值 + * @return + */ + public static ProductModuleCategoryEnum getByCode(String code) { + return Arrays.stream(values()).filter(l -> l.getCode().equals(code)).findFirst().orElse(null); + } + + public static String getDescByCode(String code) { + return Optional.ofNullable(getByCode(code)).map(ProductModuleCategoryEnum::getDesc).orElse(null); + } + + public static List ALL_CATEGORY_CODE = Lists.newArrayList(PRODUCT_VERSION.getCode(), ADD_VALUE_SERVICE.getCode(), GENERAL_SERVICE.getCode(), HARD_WARE.getCode()); +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/enums/WorkspaceTypeCodeEnum.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/enums/WorkspaceTypeCodeEnum.java new file mode 100644 index 00000000..339a0504 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/enums/WorkspaceTypeCodeEnum.java @@ -0,0 +1,72 @@ +package cn.axzo.tyr.client.model.enums; + +import com.baomidou.mybatisplus.annotation.EnumValue; +import com.fasterxml.jackson.annotation.JsonValue; +import com.google.common.collect.Lists; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/5/6 + */ +@Getter +@AllArgsConstructor +public enum WorkspaceTypeCodeEnum { + + /** + * 企业 + */ + GENERAL_ENT("1", "企业"), + + /** + * 项目 + */ + GENERAL_PROJECT("2", "项目"), + + /** + * 政务 + */ + GOVERNMENT("3", "政务"), + + /** + * 班组 + */ + TEAM("5", "班组"), + + /** + * OMS + */ + OMS("6", "OMS"), + + ; + + @EnumValue + @JsonValue + private String code; + private String desc; + + /** + * 通过value值获取枚举类型 + * + * @param code code值 + * @return + */ + public static WorkspaceTypeCodeEnum getByCode(String code) { + return Arrays.stream(values()).filter(l -> l.getCode().equals(code)).findFirst().orElse(null); + } + + public static String getDescByCode(String code) { + return Optional.ofNullable(getByCode(code)).map(WorkspaceTypeCodeEnum::getDesc).orElse(null); + } + + /** + * 后台允许新增的租户类型 + */ + public static final List ALLOW_ADD_WORKSPACE_TYPE_CODE_ENUM = Lists.newArrayList(GENERAL_ENT, GENERAL_PROJECT, GOVERNMENT); +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/permission/DeletePermissionPointRequest.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/permission/DeletePermissionPointRequest.java new file mode 100644 index 00000000..a24a28da --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/permission/DeletePermissionPointRequest.java @@ -0,0 +1,19 @@ +package cn.axzo.tyr.client.model.permission; + +import cn.axzo.tyr.client.model.vo.OperatorRequest; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotNull; + +/** + * @author yanglin + */ +@Setter +@Getter +public class DeletePermissionPointRequest extends OperatorRequest { + + @NotNull + private Long permissionId; + +} \ No newline at end of file diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/permission/PermissionPointDTO.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/permission/PermissionPointDTO.java index 2c5959a8..f425b8b6 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/permission/PermissionPointDTO.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/permission/PermissionPointDTO.java @@ -1,5 +1,6 @@ package cn.axzo.tyr.client.model.permission; +import cn.axzo.tyr.client.model.vo.Operator; import lombok.Data; import javax.validation.constraints.NotBlank; @@ -160,6 +161,12 @@ public class PermissionPointDTO { /** 业务编码 **/ private String businessNo; + private Operator operator; + + public Operator determineOperator() { + return operator == null ? Operator.absent() : operator; + } + public Long mergeFitOuTypeBit() { if (this.fitOuTypeList == null || this.fitOuTypeList.isEmpty()) { return null; diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/OldUpdateFeatureRelationRequestV2.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/OldUpdateFeatureRelationRequestV2.java new file mode 100644 index 00000000..81276b75 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/OldUpdateFeatureRelationRequestV2.java @@ -0,0 +1,25 @@ +package cn.axzo.tyr.client.model.product; + +import cn.axzo.tyr.client.model.vo.OperatorRequest; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author yanglin + */ +@Setter +@Getter +public class OldUpdateFeatureRelationRequestV2 extends OperatorRequest { + + private List relations; + private Map externalLogs = new ConcurrentHashMap<>(); + + public void addExternalLogThreadSafe(String key, Object value) { + externalLogs.put(key, value); + } + +} \ No newline at end of file diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductDetailReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductDetailReq.java new file mode 100644 index 00000000..3f0dbec4 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductDetailReq.java @@ -0,0 +1,34 @@ +package cn.axzo.tyr.client.model.product; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/6/20 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ProductDetailReq { + + /** + * 产品ID + */ + @NotNull(message = "产品ID不能为空") + @Min(value = 1, message = "") + private Long productId; + + /** + * 是否查询功能权限点 + */ + @Builder.Default + private Boolean queryFeatureScope = Boolean.FALSE; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductFeatureRelationSearchReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductFeatureRelationSearchReq.java index 169ba66b..47a565c9 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductFeatureRelationSearchReq.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductFeatureRelationSearchReq.java @@ -34,4 +34,8 @@ public class ProductFeatureRelationSearchReq { */ private String dictCode; + /** + * 关系类型 + */ + private Integer type; } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductFeatureRelationUpdateReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductFeatureRelationUpdateReq.java index 6ed6fa36..436d56bb 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductFeatureRelationUpdateReq.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductFeatureRelationUpdateReq.java @@ -41,4 +41,6 @@ public class ProductFeatureRelationUpdateReq { * 权限点 ID */ private List featureIds = new ArrayList<>(); + + private Integer featureRelationType; } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductFeatureRelationVO.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductFeatureRelationVO.java index 2f7cf654..e8a962c9 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductFeatureRelationVO.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductFeatureRelationVO.java @@ -40,4 +40,9 @@ public class ProductFeatureRelationVO { * 权限点 ID */ private Long featureId; + + /** + * 关联类型(0:saas_feature,1:saas_feature_resource) + */ + private Integer type; } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductSearchPageReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductSearchPageReq.java index 6f16df0d..42df822a 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductSearchPageReq.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductSearchPageReq.java @@ -27,9 +27,18 @@ public class ProductSearchPageReq extends PageRequest { */ private Long dictWorkspaceTypeId; + /** + * 租户编码(工作台编码) + */ + private Long dictWorkspaceTypeCode; + /** * 状态 */ private Integer status; + /** + * 产品类型 + */ + private String productCategory; } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductUpdateReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductUpdateReq.java index fef6bc42..07891792 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductUpdateReq.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductUpdateReq.java @@ -1,5 +1,6 @@ package cn.axzo.tyr.client.model.product; +import cn.axzo.tyr.client.model.vo.Operator; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -69,4 +70,9 @@ public class ProductUpdateReq { */ private List ouTypes; + private Operator operator; + + public Operator determineOperator() { + return operator == null ? Operator.absent() : operator; + } } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductVO.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductVO.java index 8530247b..c35f4baf 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductVO.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/product/ProductVO.java @@ -8,6 +8,7 @@ import lombok.experimental.Accessors; import java.util.Date; import java.util.List; +import java.util.Set; /** * 产品模型 @@ -97,4 +98,148 @@ public class ProductVO { */ private List ouTypes; + /** + * 产品类型 + * PRODUCT_VERSON:产品版本类型、ADD_VALUE_SERVICE:增值服务类型、 + * GENERAL_SERVICE:通用产品类型、HARD_WARE:硬件产品类型 + */ + private String category; + + /** + * 产品类型名称 + */ + private String productCategoryDesc; + + /** + * 版本升级序列(数字越小,版本越低,不能降级,只能升级) <企业、项目产品> + */ + private Integer version; + + /** + * 人数上限 <企业、项目产品> + */ + private Integer maxPersonCount; + + /** + * 最大项目数 <企业产品> + */ + private Integer maxWorkspaceCount; + + /** + * 价格(单位:分) + */ + private Long price; + + /** + * 产品详情 + */ + private List skus; + + /** + * 素材<仅硬件产品支持> + */ + private Material material; + + /** + * 功能范围(政务产品,只取根节点的featureId,后台取拉群所选根节点的所有子节点featureId) + */ + private List featureScopes; + + /** + * 新老产品标记,前端确认查询权限树的接口。0:旧权限,1:新权限树 + */ + private Integer relationType; + + /** + * 功能范围(非政务产品) + */ + private List relations; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Sku { + + /** + * SKU名称 + */ + private String skuName; + + /** + * 规格型号 + */ + private String model; + + /** + * 数量 + */ + private Integer count; + + /** + * 单位 + */ + private String unit; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Material { + + /** + * 图片 + */ + private List images; + + /** + * 视频 + */ + private List videos; + + /** + * 详情大图 + */ + private List detailImages; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class FeatureScope { + + /** + * 政务端featureResourceId + */ + private Long governmentFeatureResourceId; + + /** + * 功能资源名称 + */ + private String featureResourceName; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class ProductFeatureRelation { + + /** + * 单位类型 + */ + private Long dictCodeId; + + /** + * 总数 + */ + private Integer totalCount; + + /** + * 勾选的最末级 feature 的 ID 集合 + */ + private List featureIds; + } } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/DeleteFeatureResourceReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/DeleteFeatureResourceReq.java new file mode 100644 index 00000000..73d74382 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/DeleteFeatureResourceReq.java @@ -0,0 +1,21 @@ +package cn.axzo.tyr.client.model.req; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DeleteFeatureResourceReq { + + @NotNull(message = "featureId不能为空") + private Long featureId; + + @NotNull(message = "operatorId不能为空") + private Long operatorId; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/FeatureIdPair.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/FeatureIdPair.java new file mode 100644 index 00000000..3991f6d0 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/FeatureIdPair.java @@ -0,0 +1,22 @@ +package cn.axzo.tyr.client.model.req; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Set; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FeatureIdPair { + + /** + * 区分新老菜单资源树 + */ + private Integer type; + + private Set featureIds; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/FeatureResourceTreeSaveReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/FeatureResourceTreeSaveReq.java index 22bdd16c..58e84c8b 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/FeatureResourceTreeSaveReq.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/FeatureResourceTreeSaveReq.java @@ -22,7 +22,7 @@ public class FeatureResourceTreeSaveReq extends BaseFeatureResourceDO { /** 路由地址 **/ private String linkUrl; - /** 路由类型 1-PC 2-小程序 3-原生 **/ + /** 路由类型 1-PC 2-小程序 3-原生 4-h5**/ private Integer linkType; /** APP适配参数 **/ @@ -34,6 +34,16 @@ public class FeatureResourceTreeSaveReq extends BaseFeatureResourceDO { /** 授权类型 0-全部角色 1-指定角色 **/ private Integer authType; + /** + * 应用范围(租户类型):1:企业工作台 2;项目工作台 + */ + private Long workspaceType; + + /** + * 最低版本序列,主要支持版本灰度策略 + */ + private Integer version; + /** 页面组件对象 **/ private List componentSaveReqList; diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/FeatureRoleRelationReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/FeatureRoleRelationReq.java index 89109db0..2afd9e5a 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/FeatureRoleRelationReq.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/FeatureRoleRelationReq.java @@ -29,10 +29,17 @@ public class FeatureRoleRelationReq { /** 功能Id **/ private Long featureId; + /** + * 菜单资源的类型 + */ + private Integer featureType; + /** 应用的角色Id列表 **/ private List roleIds; /** 授权类型 0-全部角色 1-指定角色 **/ private Integer authType; + + private String terminal; } } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/GetFeatureResourceTreeReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/GetFeatureResourceTreeReq.java index c8a33df3..8aff7dc8 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/GetFeatureResourceTreeReq.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/GetFeatureResourceTreeReq.java @@ -27,4 +27,16 @@ public class GetFeatureResourceTreeReq { /** feature类型列表 **/ private List featureTypes; + + /** + * 应用范围(原租户类型):1:企业租户 2;项目租户 + * 只有页面和应用会配置应用范围 + */ + private Long workspaceType; + + /** + * 是否过滤掉空菜单(没有子页面和组件的菜单过滤不返回) + */ + @Builder.Default + private Boolean needFilterBlankMenu = Boolean.FALSE; } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/GetPageElementReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/GetPageElementReq.java new file mode 100644 index 00000000..56d0c85d --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/GetPageElementReq.java @@ -0,0 +1,43 @@ +package cn.axzo.tyr.client.model.req; + +import com.google.common.collect.Lists; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/6/14 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GetPageElementReq { + + /** + * 页面的featureResourceId + */ + @NotNull(message = "页面的featureResourceId不能为空") + @Min(value = 1, message = "页面的featureResourceId有误") + private Long featureResourceId; + + /** + * 是否只查询组件已选择的元素 + */ + @Builder.Default + private Boolean querySelectedOnly = Boolean.FALSE; + + /** + * 查询的关联类型,默认全查 + * 如果要过滤页面路由的,relationTypes传[0] + */ + @Builder.Default + private List relationTypes = Lists.newArrayList(0, 1); +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/GetUserHasPermissionPageElementReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/GetUserHasPermissionPageElementReq.java new file mode 100644 index 00000000..16788bd8 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/GetUserHasPermissionPageElementReq.java @@ -0,0 +1,45 @@ +package cn.axzo.tyr.client.model.req; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/6/17 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GetUserHasPermissionPageElementReq { + + /** 页面的元素 **/ + @NotBlank(message = "页面的元素不能为空") + private String pageElementCode; + + /** 登录端 **/ + @NotBlank(message = "登录端不能为空") + private String terminal; + + /** 人员ID **/ + @NotNull(message = "人员ID不能为空") + @Min(value = 1, message = "人员ID有误") + private Long personId; + + /** 单位ID **/ + @NotNull(message = "单位ID不能为空") + @Min(value = 1, message = "单位ID有误") + private Long ouId; + + /** 租户ID **/ + @NotNull(message = "租户ID不能为空") + @Min(value = 1, message = "租户ID有误") + private Long workspaceId; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/IdentityAuthReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/IdentityAuthReq.java index 099f3495..368f705f 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/IdentityAuthReq.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/IdentityAuthReq.java @@ -1,9 +1,8 @@ package cn.axzo.tyr.client.model.req; import cn.axzo.framework.auth.domain.TerminalInfo; -import cn.axzo.tyr.client.common.enums.WorkspaceJoinType; -import cn.axzo.tyr.client.model.enums.FeatureType; import cn.axzo.tyr.client.model.enums.IdentityType; +import cn.axzo.tyr.client.model.res.IdentityAuthRes; import cn.hutool.core.collection.CollectionUtil; import lombok.AllArgsConstructor; import lombok.Builder; @@ -13,18 +12,11 @@ import lombok.NoArgsConstructor; import javax.validation.Valid; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; -import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import static cn.axzo.tyr.client.common.enums.WorkspaceJoinType.ENT_GROUP; -import static cn.axzo.tyr.client.common.enums.WorkspaceJoinType.ENT_TEAM; -import static cn.axzo.tyr.client.common.enums.WorkspaceJoinType.PROJ_GROUP; -import static cn.axzo.tyr.client.common.enums.WorkspaceJoinType.PROJ_TEAM; - /** * @author tanjie@axzo.cn * @date 2023/10/13 15:23 @@ -72,6 +64,22 @@ public class IdentityAuthReq { /** 是否使用缓存 - 默认true **/ @Builder.Default private boolean useCache = true; + public IdentityAuthRes toEmpty() { + IdentityAuthRes result = new IdentityAuthRes(); + result.setIdentity(this.getIdentityId()); + result.setIdentityType(this.getIdentityType()); + result.setPersonId(this.getPersonId()); + + List permissions = this.getWorkspaceOusPairs().stream() + .map(workspaceOuPair -> IdentityAuthRes.WorkspacePermission.builder() + .workspaceId(workspaceOuPair.getWorkspaceId()) + .ouId(workspaceOuPair.getOuId()) + .build()) + .collect(Collectors.toList()); + result.setPermissions(permissions); + + return result; + } public void distinctOUWorkspacePair() { if (CollectionUtil.isEmpty(this.workspaceOusPairs)) { diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ListPermissionFromRoleGroupReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ListPermissionFromRoleGroupReq.java index dd07edee..f6245c80 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ListPermissionFromRoleGroupReq.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ListPermissionFromRoleGroupReq.java @@ -44,6 +44,12 @@ public class ListPermissionFromRoleGroupReq { /** 角色组ID **/ private List roleGroupIds; + /** + * 原接口使用方有的需要2,有的需要3,当时加这个出了问题,默认为3 + * 1-仅查当前code 2-对应code角色组及子级角色组 3-仅对应code角色组的子级 + */ + private Integer categoryType; + @Data @Builder @AllArgsConstructor diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ListRoleReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ListRoleReq.java index 0669cebf..20688036 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ListRoleReq.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ListRoleReq.java @@ -13,23 +13,63 @@ import java.util.List; @AllArgsConstructor public class ListRoleReq { + /** + * 角色id + */ private List roleIds; + /** + * 租户类型:DictWorkSpaceTypeEnum + */ private Integer workspaceType; + /** + * 是否显示 + */ private Boolean isDisplay; + /** + * 角色权限码 + */ private String roleCode; + /** + * 租户id + */ private List workspaceIds; + /** + * 单位id + */ private List ouIds; + /** + * 是否启用 + */ private Boolean enabled; + /** + * 角色类型:RoleTypeEnum + * super_admin:超级管理员 + * admin:管理员 + * init:初始化内置角色-标准角色 + * auto_own:虚拟角色(自定义权限使用) + * common:自定义角色 + */ private List roleTypes; + /** + * 是否需要角色下的权限信息 + */ private Boolean needPermission; + /** + * 是否需要角色对应的角色分组信息 + */ private Boolean needRoleGroup; + + /** + * 是否需要角色对应的用户信息 + */ + private Boolean needRoleUser; } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ListSaasRoleGroupParam.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ListSaasRoleGroupParam.java new file mode 100644 index 00000000..d343583b --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ListSaasRoleGroupParam.java @@ -0,0 +1,29 @@ +package cn.axzo.tyr.client.model.req; + +import cn.axzo.foundation.dao.support.wrapper.CriteriaField; +import cn.axzo.foundation.dao.support.wrapper.Operator; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + + +@SuperBuilder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ListSaasRoleGroupParam { + + @CriteriaField(field = "workspaceTypeCode", operator = Operator.EQ) + private Integer workspaceTypeCode; + + @CriteriaField(field = "workspaceId", operator = Operator.EQ) + private Long workspaceId; + + @CriteriaField(field = "ouId", operator = Operator.EQ) + private Long ouId; + + @CriteriaField(ignore = true) + private Boolean needRole; + +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ModifyPageElementRelationDTO.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ModifyPageElementRelationDTO.java new file mode 100644 index 00000000..d085c509 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ModifyPageElementRelationDTO.java @@ -0,0 +1,46 @@ +package cn.axzo.tyr.client.model.req; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/6/19 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ModifyPageElementRelationDTO { + + /** + * 操作人ID + */ + private Long operatorId; + + /** + * 组件所属端 + */ + private String terminal; + + /** + * 组件code + */ + private String featureResourceUniCode; + + /** + * 页面元素编码列表 + */ + private List pageElementCodes; + + /** + * 绑定关系 + */ + @Builder.Default + private Integer relationType = 0; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PageElementFeatureResourceRelationReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PageElementFeatureResourceRelationReq.java new file mode 100644 index 00000000..b4f9abc2 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PageElementFeatureResourceRelationReq.java @@ -0,0 +1,40 @@ +package cn.axzo.tyr.client.model.req; + +import cn.axzo.foundation.dao.support.wrapper.CriteriaField; +import cn.axzo.foundation.dao.support.wrapper.Operator; +import cn.axzo.foundation.page.IPageReq; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Set; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PageElementFeatureResourceRelationReq implements IPageReq { + + @CriteriaField(ignore = true) + Integer page; + + @CriteriaField(ignore = true) + Integer pageSize; + + /** + * 排序:使用示例,createTime__DESC + */ + @CriteriaField(ignore = true) + List sort; + + @CriteriaField(field = "featureResourceUniCode", operator = Operator.IN) + private List featureResourceUniCodes; + + @CriteriaField(field = "pageElementCode", operator = Operator.IN) + private Set pageElementCodes; + + @CriteriaField(field = "terminal", operator = Operator.EQ) + private String terminal; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PageElementReportReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PageElementReportReq.java new file mode 100644 index 00000000..668a982d --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PageElementReportReq.java @@ -0,0 +1,78 @@ +package cn.axzo.tyr.client.model.req; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import java.util.List; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/6/14 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PageElementReportReq { + + /** + * 端 + */ + @NotBlank(message = "端信息不能为空") + private String terminal; + + /** + * 上报秘钥 + */ + @NotBlank(message = "上报秘钥不能为空") + private String secretKey; + + /** + * 创建人名 + */ + @NotBlank(message = "创建人名不能为空") + private String createName; + + /** + * 页面元素列表 + */ + @NotEmpty(message = "页面元素不能为空") + private List pageElements; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class PageElement { + + /** + * 元素编码 + */ + private String code; + + /** + * 元素名称 + */ + private String name; + + /** + * 元素类型(PAGE:页面,COMPONENT:页面里的组件) + */ + private String type; + + /** + * 路由地址 + */ + private String linkUrl; + + /** + * 子元素(只包含一级) + */ + private List children; + } +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PagePgroupPermissionRelationReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PagePgroupPermissionRelationReq.java new file mode 100644 index 00000000..3039feb2 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PagePgroupPermissionRelationReq.java @@ -0,0 +1,61 @@ +package cn.axzo.tyr.client.model.req; + +import cn.axzo.foundation.dao.support.wrapper.CriteriaField; +import cn.axzo.foundation.dao.support.wrapper.Operator; +import cn.axzo.foundation.page.IPageReq; +import cn.axzo.tyr.client.common.enums.FeatureResourceType; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PagePgroupPermissionRelationReq implements IPageReq { + + @CriteriaField(ignore = true) + Integer page; + + @CriteriaField(ignore = true) + Integer pageSize; + + /** + * 排序:使用示例,createTime__DESC + */ + @CriteriaField(ignore = true) + List sort; + + @CriteriaField(field = "id", operator = Operator.IN) + private List ids; + + @CriteriaField(field = "groupId", operator = Operator.IN) + private List groupIds; + + @CriteriaField(field = "featureId", operator = Operator.IN) + private List featureIds; + + @CriteriaField(field = "type", operator = Operator.EQ) + private Integer type; + + /** + * 查询菜单树节点类型 + */ + @CriteriaField(ignore = true) + private List featureResourceTypes; + + /** + * 端,查询权限点时,会根据端过滤,增加效率,目前只有CMS端的新版本才冗余了端 + */ + @CriteriaField(ignore = true) + private String terminal; + + /** + * 新老版本两个情况,可以配对查询 + */ + @CriteriaField(ignore = true) + private List featureIdPairs; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PageProductFeatureRelationReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PageProductFeatureRelationReq.java new file mode 100644 index 00000000..f5381af2 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PageProductFeatureRelationReq.java @@ -0,0 +1,33 @@ +package cn.axzo.tyr.client.model.req; + +import cn.axzo.foundation.dao.support.wrapper.CriteriaField; +import cn.axzo.foundation.dao.support.wrapper.Operator; +import cn.axzo.foundation.page.IPageReq; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PageProductFeatureRelationReq implements IPageReq { + + @CriteriaField(ignore = true) + Integer page; + + @CriteriaField(ignore = true) + Integer pageSize; + + /** + * 排序:使用示例,createTime__DESC + */ + @CriteriaField(ignore = true) + List sort; + + @CriteriaField(field = "id", operator = Operator.IN) + private List ids; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PageQueryElementReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PageQueryElementReq.java new file mode 100644 index 00000000..21706a88 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PageQueryElementReq.java @@ -0,0 +1,49 @@ +package cn.axzo.tyr.client.model.req; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; +import java.util.List; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/6/14 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PageQueryElementReq { + + /** + * 端 + */ + @NotBlank(message = "端不能为空") + private String terminal; + + /** + * 页面元素组件类型 + */ + private List elementTypes; + + /** + * 搜索条件 + */ + private String searchKey; + + /** + * 页码 + */ + @Builder.Default + private Long page = 1L; + + /** + * 每页大小 + */ + @Builder.Default + private Long pageSize = 100L; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PageSaasFeatureResourceReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PageSaasFeatureResourceReq.java new file mode 100644 index 00000000..02de287b --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PageSaasFeatureResourceReq.java @@ -0,0 +1,81 @@ +package cn.axzo.tyr.client.model.req; + +import cn.axzo.foundation.dao.support.wrapper.CriteriaField; +import cn.axzo.foundation.dao.support.wrapper.Operator; +import cn.axzo.foundation.page.IPageReq; +import cn.axzo.foundation.page.PageResp; +import cn.axzo.tyr.client.common.enums.FeatureResourceType; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PageSaasFeatureResourceReq implements IPageReq { + + @CriteriaField(ignore = true) + Integer page; + + @CriteriaField(ignore = true) + Integer pageSize; + + /** + * 排序:使用示例,createTime__DESC + */ + @CriteriaField(ignore = true) + List sort; + + @CriteriaField(field = "id", operator = Operator.IN) + private List ids; + + /** + * 授权类型0-全部角色 1-指定角色 + * FeatureResourceAuthType.ALL_ROLE + */ + @CriteriaField(field = "authType", operator = Operator.EQ) + private Integer authType; + + /** + * 资源所属端 + */ + @CriteriaField(field = "terminal", operator = Operator.EQ) + private String terminal; + + @CriteriaField(ignore = true) + private Long parentId; + + @CriteriaField(field = "featureType", operator = Operator.IN) + private List featureResourceTypes; + + @CriteriaField(field = "path", operator = Operator.SW) + private String path; + + /** + * CMS端saas_feature_resource.feature_codes已经废弃,后续其他端也会这样迁移 + * 新的存在saas_page_element_feature_resource_relation + */ + @CriteriaField(ignore = true) + private Boolean needFeatureCodes; + + @CriteriaField(field = "uniCode", operator = Operator.IN) + private Set uniCodes; + + @CriteriaField(ignore = true) + private Set paths; + + public PageResp toEmpty() { + return PageResp.builder() + .current(this.getPage()) + .size(this.getPageSize()) + .total(0) + .data(Collections.emptyList()) + .build(); + } +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PageSaasRoleGroupParam.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PageSaasRoleGroupParam.java new file mode 100644 index 00000000..96936ea6 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PageSaasRoleGroupParam.java @@ -0,0 +1,31 @@ +package cn.axzo.tyr.client.model.req; + +import cn.axzo.foundation.dao.support.wrapper.CriteriaField; +import cn.axzo.foundation.page.IPageReq; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.List; + + +@SuperBuilder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class PageSaasRoleGroupParam extends ListSaasRoleGroupParam implements IPageReq { + + @CriteriaField(ignore = true) + Integer page; + + @CriteriaField(ignore = true) + Integer pageSize; + + /** + * 排序:使用示例,createTime__DESC + */ + @CriteriaField(ignore = true) + List sort; + +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PermissionCheckReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PermissionCheckReq.java index c354c77a..11064145 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PermissionCheckReq.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PermissionCheckReq.java @@ -34,6 +34,8 @@ public class PermissionCheckReq { @NotNull(message = "租户ID不能为空") private Long workspaceId; - /** 登录端 **/ + /** + * 登录端,历史的cms和cmp,cm端没有给端,给了会有问题 + */ private String terminal; } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PermissionOperateLogReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PermissionOperateLogReq.java new file mode 100644 index 00000000..dfa5c3a4 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/PermissionOperateLogReq.java @@ -0,0 +1,44 @@ +package cn.axzo.tyr.client.model.req; + +import com.alibaba.fastjson.JSONObject; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.List; + +/** + * 创建新路由请求 + * + * @version V1.0 + * @author: ZhanSiHu + * @date: 2023/9/11 10:33 + */ +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PermissionOperateLogReq implements Serializable { + + + @NotBlank(message = "表名不能为空") + private String tableName; + + @NotNull(message = "操作人ID不能为空") + @Min(value = 1, message = "操作人ID有误") + private Long operatorId; + + @NotBlank(message = "场景不能为空") + private String scene; + + private String sceneId; + + private Object requestData; + + private Object operateData; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ProductSaveReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ProductSaveReq.java new file mode 100644 index 00000000..d53f501b --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ProductSaveReq.java @@ -0,0 +1,189 @@ +package cn.axzo.tyr.client.model.req; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.validation.Valid; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.List; +import java.util.Set; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/4/26 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ProductSaveReq { + + /** + * 产品ID + */ + private Long id; + + /** + * 产品名称 + */ + @NotBlank(message = "产品名称不能为空") + @Length(max = 35, message = "产品名称长度不能超过 35 个字符") + private String productName; + + /** + * 产品icon + */ + private String icon; + + /** + * 产品所属租户类型 + */ + @NotNull(message = "租户类型不能为空") + private Long dictWorkspaceTypeId; + + /** + * 产品类型 + */ + @NotBlank(message = "产品类型不能为空") + private String productCategory; + + /** + * 版本升级序列(数字越小,版本越低,不能降级,只能升级) <企业、项目产品> + */ + @Min(value = 0, message = "版本升级序列有误") + private Integer version; + + /** + * 人数上限 <企业、项目产品> + */ + @Min(value = 0, message = "人数上限有误") + private Integer maxPersonCount; + + /** + * 最大项目数 <企业产品> + */ + @Min(value = 0, message = "最大项目数有误") + private Integer maxWorkspaceCount; + + /** + * 价格(单位:分) <企业、项目产品> + */ + @Min(value = 0, message = "价格有误") + private Long price; + + /** + * SKU列表 + */ + @Valid + private List skus; + + /** + * 素材<仅硬件产品支持> + */ + private Material material; + + /** + * 功能范围(政务产品,只取根节点的featureId,后台取拉群所选根节点的所有子节点featureId) + */ + private FeatureScope featureScope; + + /** + * 描述 + */ + private String remark; + + /** + * 操作人 + */ + private Long operator; + + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Sku { + + /** + * SKU名称 + */ + private String skuName; + + /** + * 规格型号 + */ + private String model; + + /** + * 数量 + */ + @Min(value = 1, message = "数量有误") + private Integer count; + + /** + * 单位 + */ + private String unit; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Material { + + /** + * 图片素材 + */ + private List images; + + /** + * 视频素材 + */ + private List videos; + + /** + * 详情介绍大图 + */ + private List detailImages; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class FeatureScope { + + /** + * 政务端featureResourceId + */ + private List governmentFeatureResourceIds; + + /** + * 产品与功能点的关联关系 + */ + private List relations; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class ProductFeatureRelation { + /** + * 单位类型 + */ + private Long dictCodeId; + + /** + * 勾选的最末级 feature 的 ID 集合 + */ + private Set featureIds; + } +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/QuerySaasRoleGroupReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/QuerySaasRoleGroupReq.java index 6c732293..9dce117d 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/QuerySaasRoleGroupReq.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/QuerySaasRoleGroupReq.java @@ -6,6 +6,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; +import java.util.Set; @Data @AllArgsConstructor @@ -16,7 +17,7 @@ public class QuerySaasRoleGroupReq { private List ids; /** - * 项目部id(不传或者传-1查询的是标准分组) + * 项目id(不传或者传-1查询的是标准分组) */ private List workspaceIds; @@ -46,4 +47,6 @@ public class QuerySaasRoleGroupReq { private List roleIds; private Long parentId; + + private Set parentIds; } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ResourceSyncReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ResourceSyncReq.java index 88e4c9c4..759d70e7 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ResourceSyncReq.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/ResourceSyncReq.java @@ -29,4 +29,6 @@ public class ResourceSyncReq { /** 操作人personId **/ @NotNull(message = "操作人ID不能为空") private Long operatorId; + + private String traceId; } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/SaasRoleGroupDeleteRequest.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/SaasRoleGroupDeleteRequest.java new file mode 100644 index 00000000..2f35f0e6 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/SaasRoleGroupDeleteRequest.java @@ -0,0 +1,20 @@ +package cn.axzo.tyr.client.model.req; + +import cn.axzo.tyr.client.model.vo.OperatorRequest; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotEmpty; +import java.util.List; + +/** + * @author yanglin + */ +@Setter +@Getter +public class SaasRoleGroupDeleteRequest extends OperatorRequest { + + @NotEmpty(message = "ids不能为空") + private List ids; + +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/TreePermissionReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/TreePermissionReq.java new file mode 100644 index 00000000..72c00f79 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/TreePermissionReq.java @@ -0,0 +1,49 @@ +package cn.axzo.tyr.client.model.req; + +import cn.axzo.tyr.client.common.enums.FeatureResourceType; +import cn.axzo.tyr.client.model.base.WorkspaceOUPair; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.List; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class TreePermissionReq { + + @NotNull(message = "人员ID不能为空") + private Long personId; + + /** + * 端 + */ + @NotNull(message = "端不能为空") + private String terminal; + + /** + * 项目与企业 + */ + @NotEmpty(message = "项目与企业对不能为空") + private List workspaceOUPairs; + + /** + * 查询菜单树节点类型 + */ + private List featureResourceTypes; + + /** + * 菜单节点的uniCode + */ + private String uniCode; + + /** + * 是否需要返回权限码 + */ + private boolean needFeatureCodes; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/TreeProductFeatureResourceReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/TreeProductFeatureResourceReq.java new file mode 100644 index 00000000..ff65d923 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/TreeProductFeatureResourceReq.java @@ -0,0 +1,23 @@ +package cn.axzo.tyr.client.model.req; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import javax.validation.constraints.NotNull; + +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class TreeProductFeatureResourceReq { + + /** + * 租户id + */ + @NotNull(message = "workspaceId不能为空") + private Long workspaceId; + + private String terminal; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/TreeRoleReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/TreeRoleReq.java index 13d76654..50e86b69 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/TreeRoleReq.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/TreeRoleReq.java @@ -25,7 +25,7 @@ public class TreeRoleReq { private Boolean isDisplay; /** - * 项目部id(不传或者传-1查询的是标准分组) + * 项目id(不传或者传-1查询的是标准分组) */ private List workspaceIds; diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/UpdateProductStatusReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/UpdateProductStatusReq.java new file mode 100644 index 00000000..1640e810 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/req/UpdateProductStatusReq.java @@ -0,0 +1,35 @@ +package cn.axzo.tyr.client.model.req; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/4/26 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class UpdateProductStatusReq { + + /** + * 产品ID + */ + @NotNull(message = "产品ID不能为空") + @Min(value = 1, message = "产品ID有误") + private Long id; + + /** + * 状态 + */ + @NotNull(message = "状态不能为空") + @Min(value = 0, message = "状态有误") + @Max(value = 1, message = "状态有误") + private Integer status; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/FeatureResourceDTO.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/FeatureResourceDTO.java index 59f67bca..7d01dc64 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/FeatureResourceDTO.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/FeatureResourceDTO.java @@ -1,5 +1,6 @@ package cn.axzo.tyr.client.model.res; +import cn.axzo.tyr.client.model.base.FeatureResourceExtraDO; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -7,6 +8,7 @@ import lombok.experimental.SuperBuilder; import java.io.Serializable; import java.util.Date; +import java.util.List; /** * 功能资源DTO @@ -38,7 +40,7 @@ public class FeatureResourceDTO implements Serializable { private String featureName; /** - * 资源类型1-菜单 2-页面 3-应用入口 4-组件 + * 资源类型1-菜单 2-页面 3-应用入口 4-组件 5-root 6-分组 */ private Integer featureType; @@ -88,7 +90,7 @@ public class FeatureResourceDTO implements Serializable { private String linkUrl; /** - * 路由类型1-PC 2-小程序 3-原生 + * 路由类型1-PC 2-小程序 3-原生 4-h5 */ private Integer linkType; @@ -110,7 +112,7 @@ public class FeatureResourceDTO implements Serializable { /** * 扩展字段 */ - private String extra; + private FeatureResourceExtraDO extra; /** * 授权类型0-全部角色 1-指定角色 @@ -136,4 +138,24 @@ public class FeatureResourceDTO implements Serializable { * 更新人 */ private Long updateBy; + + /** + * 应用范围(租户类型):1:企业工作台 2;项目工作台 + */ + private Long workspaceType; + + /** + * 最低版本序列,主要支持版本灰度策略 + */ + private Integer version; + + /** + * 唯一编码,用于pre环境菜单同步 + */ + private String uniCode; + + /** + * 页面元素对象 + */ + private List pageElements; } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/FeatureResourceTreeNode.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/FeatureResourceTreeNode.java index 73cf25c6..a7ef28e6 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/FeatureResourceTreeNode.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/FeatureResourceTreeNode.java @@ -24,6 +24,8 @@ public class FeatureResourceTreeNode extends FeatureResourceDTO implements IBase private List children; + private List roleCodes; + @JsonIgnore @Override public Long getNodeCode() { diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/GetUserHasPermissionPageElementResp.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/GetUserHasPermissionPageElementResp.java new file mode 100644 index 00000000..d6ea631b --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/GetUserHasPermissionPageElementResp.java @@ -0,0 +1,25 @@ +package cn.axzo.tyr.client.model.res; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/6/17 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GetUserHasPermissionPageElementResp { + + /** + * 页面元素的elementCodes + */ + private List pageElementCodes; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/GovernmentTerminalResp.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/GovernmentTerminalResp.java new file mode 100644 index 00000000..ee480a1a --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/GovernmentTerminalResp.java @@ -0,0 +1,28 @@ +package cn.axzo.tyr.client.model.res; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/4/30 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GovernmentTerminalResp { + + /** + * 功能资源ID + */ + private Long featureResourceId; + + /** + * 功能资源名称 + */ + private String featureResourceName; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/IdentityAuthRes.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/IdentityAuthRes.java index 6ab3c6d6..07a46c40 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/IdentityAuthRes.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/IdentityAuthRes.java @@ -61,6 +61,11 @@ public class IdentityAuthRes { // private FeatureType featureType; private String terminal; + + /** + * 应用范围(租户类型):1:企业工作台 2;项目工作台 + */ + private Long workspaceType; } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/ListPermissionFromRoleGroupResp.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/ListPermissionFromRoleGroupResp.java index 02f7b6f8..d2547c25 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/ListPermissionFromRoleGroupResp.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/ListPermissionFromRoleGroupResp.java @@ -1,9 +1,11 @@ package cn.axzo.tyr.client.model.res; import cn.axzo.tyr.client.model.enums.IdentityType; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; -import java.util.List; import java.util.Set; /** @@ -55,6 +57,11 @@ public class ListPermissionFromRoleGroupResp { */ private String roleName; + /** + * 角色编码 + */ + private String roleCode; + /** * 权限集ID */ @@ -66,8 +73,22 @@ public class ListPermissionFromRoleGroupResp { */ private Set simpleFeatureInfos; + /** + * 权限点及是否为新旧权限点 + */ + private Set featureInfos; + + @Data + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class FeatureInfo { + private Long featureId; + /** 0:saas_feature,1:saas_feature_resource **/ + private Integer relationType; + } } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/PageElementBasicDTO.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/PageElementBasicDTO.java new file mode 100644 index 00000000..57a7f13e --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/PageElementBasicDTO.java @@ -0,0 +1,44 @@ +package cn.axzo.tyr.client.model.res; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/6/19 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PageElementBasicDTO { + + /** + * 页面、元素ID + */ + private Long id; + + /** + * 组件编码 + */ + private String uniCode; + + /** + * 页面、元素编码 + */ + private String code; + + /** + * 页面、元素名称 + */ + private String name; + + /** + * 绑定关系 + * @see cn.axzo.tyr.client.common.enums.PageElementFeatureResourceRelationTypeEnum + */ + private Integer relationType; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/PageElementResp.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/PageElementResp.java new file mode 100644 index 00000000..c32c9411 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/PageElementResp.java @@ -0,0 +1,60 @@ +package cn.axzo.tyr.client.model.res; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/6/14 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PageElementResp { + + /** + * 元素ID + */ + private Long id; + + /** + * 元素的组编码 + */ + private String groupCode; + + /** + * 元素编码 + */ + private String code; + + /** + * 元素名称 + */ + private String name; + + /** + * 元素类型 + */ + private String type; + + /** + * 页面路由地址 + */ + private String linkUrl; + + /** + * 是否已勾选 + */ + private Boolean selected; + + /** + * 子元素(只包含一级) + */ + private List children; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/ProductFeatureResourceResp.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/ProductFeatureResourceResp.java new file mode 100644 index 00000000..95224b74 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/ProductFeatureResourceResp.java @@ -0,0 +1,80 @@ +package cn.axzo.tyr.client.model.res; + +import cn.axzo.basics.common.model.IBaseTree; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class ProductFeatureResourceResp implements IBaseTree { + + /** + * 权限id + */ + private Long id; + + /** + * 权限编码 + */ + private String featureCode; + + /** + * 权限名字 + */ + private String featureName; + + /** + * 资源类型1-菜单 2-页面 3-应用入口 4-组件 + */ + private Integer featureType; + + /** + * 资源所属端 + */ + private String terminal; + + /** + * 展示顺序 + */ + private Integer displayOrder; + + /** + * 父节点id + */ + private Long parentId; + + /** + * 子节点信息 + */ + private List children; + + @JsonIgnore + @Override + public Long getNodeCode() { + return this.getId(); + } + + @JsonIgnore + @Override + public Long getParentNodeCode() { + return this.getParentId(); + } + + @JsonIgnore + @Override + public List getNodeChildren() { + return this.children; + } + + @Override + public void setNodeChildren(List nodeChildren) { + this.children = nodeChildren; + } +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/RoleTreeRes.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/RoleTreeRes.java index e0f1aabe..7f7b423a 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/RoleTreeRes.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/RoleTreeRes.java @@ -23,8 +23,21 @@ public class RoleTreeRes { */ private String workspaceTypeCode; + /** + * 租户类型 + */ private String workspaceType; + /** + * 租户id + */ + private Long workspaceId; + + /** + * 单位id + */ + private Long ouId; + /** * 名字 */ diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/SaasFeatureResourceResp.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/SaasFeatureResourceResp.java new file mode 100644 index 00000000..d8905e45 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/SaasFeatureResourceResp.java @@ -0,0 +1,146 @@ +package cn.axzo.tyr.client.model.res; + +import cn.axzo.tyr.client.model.base.FeatureResourceExtraDO; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; +import java.util.Set; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SaasFeatureResourceResp { + + private Long id; + + private Date createAt; + + private Date updateAt; + + /** + * 资源编码-权限码 + */ + private Set featureCodes; + + /** + * 资源名称 + */ + private String featureName; + + /** + * 资源类型1-菜单 2-页面 3-应用入口 4-组件;5-root节点 + */ + private Integer featureType; + + /** + * 资源所属端 + */ + private String terminal; + + /** + * 组件细分类型 1-跳转子页面 2-跳转公共组件 3-弹出窗口 4-下拉项 5-操作按钮 6-数据卡片 7-站外跳转 + */ + private Integer componentType; + + /** + * 上级资源ID + */ + private Long parentId; + + /** + * 资源ID层级路径, 逗号分隔 + */ + private String path; + + /** + * 展示顺序 + */ + private Integer displayOrder; + + /** + * 资源状态 0-隐藏 1-展示 + */ + private Integer status; + + /** + * 资源图标 + */ + private String icon; + + /** + * 跳转类型 1-站内跳转 2-站外跳转 + */ + private Integer redirectType; + + /** + * 资源跳转URI + */ + private String linkUrl; + + /** + * 路由类型1-PC 2-小程序 3-原生 + */ + private Integer linkType; + + /** + * APP适配参数 + */ + private String linkExt; + + /** + * 小程序id + */ + private Integer appItemId; + + /** + * 资源同步版本 + */ + private Integer syncVersion; + + /** + * 扩展字段 + */ + @TableField(value = "extra", typeHandler = FastjsonTypeHandler.class) + private FeatureResourceExtraDO extra; + + /** + * 授权类型0-全部角色 1-指定角色 + */ + private Integer authType; + + /** + * 子级鉴权类型 0-不鉴权1-鉴权 + */ + private Integer subAuthType; + + /** + * 创建人 + */ + private Long createBy; + + /** + * 更新人 + */ + private Long updateBy; + + /** + * 应用范围(租户类型):1:企业工作台 2;项目工作台 + */ + private Long workspaceType; + + /** + * 最低版本序列,主要支持版本灰度策略 + */ + private Integer version; + + /** + * 唯一编码,用于pre环境菜单同步 + */ + private String uniCode; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/SaasPermissionRelationRes.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/SaasPermissionRelationRes.java new file mode 100644 index 00000000..f8ba9b09 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/SaasPermissionRelationRes.java @@ -0,0 +1,28 @@ +package cn.axzo.tyr.client.model.res; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SaasPermissionRelationRes { + + /** + * 菜单资源树节点id + */ + private Long featureId; + + /** + * 新旧菜单资源数标识 + */ + private Integer type; + + /** + * 菜单资源树节点类型 + */ + private Integer featureType; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/SaasPermissionRes.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/SaasPermissionRes.java index 5e007685..2cf6fc65 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/SaasPermissionRes.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/SaasPermissionRes.java @@ -17,4 +17,10 @@ public class SaasPermissionRes { * 资源编码-权限码 */ private String featureCode; + + /** + * 资源所属端 + */ + private String terminal; + } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/SaasRoleGroupDTO.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/SaasRoleGroupDTO.java new file mode 100644 index 00000000..ca382e38 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/SaasRoleGroupDTO.java @@ -0,0 +1,67 @@ +package cn.axzo.tyr.client.model.res; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SaasRoleGroupDTO { + + private Long id; + + /** + * 工作台类型字典code + */ + private String workspaceTypeCode; + + /** + * 单位类型字典code + */ + private String ouTypeCode; + + /** + * 名称 + */ + private String name; + + /** + * 所属工作台id + */ + private Long workspaceId; + + /** + * 所属单位id + */ + private Long ouId; + + /** + * 排序 + */ + private Integer sort; + + /** + * 分组CODE + */ + private String code; + + /** + * 分类CODE, 用于代班长,小组长的权限分类。 + */ + private String categoryCode; + + /** + * 上级分组id + */ + private Long parentId; + + /** + * 角色信息 + */ + private List saasRoles; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/SaasRoleRes.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/SaasRoleRes.java index dab00cf6..cc686432 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/SaasRoleRes.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/SaasRoleRes.java @@ -1,5 +1,6 @@ package cn.axzo.tyr.client.model.res; +import cn.axzo.tyr.client.model.roleuser.dto.SaasRoleUserV2DTO; import cn.axzo.tyr.client.model.vo.SaasPermissionGroupVO; import lombok.AllArgsConstructor; import lombok.Builder; @@ -107,4 +108,31 @@ public class SaasRoleRes { * 角色权限 */ private List saasPermissions; + + /** + * 角色对应的人员信息 + */ + private List saasRoleUsers; + + /** + * 角色关联的权限点id信息,没有featureCode,直接查询的pgPermissionRelation + */ + private List permissionRelations; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class SaasRoleUser { + + /** + * 用户id + */ + private Long personId; + + /** + * 用户名字 + */ + private String realName; + } } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/TreePermissionResp.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/TreePermissionResp.java new file mode 100644 index 00000000..198a887e --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/TreePermissionResp.java @@ -0,0 +1,110 @@ +package cn.axzo.tyr.client.model.res; + + +import cn.axzo.basics.common.model.IBaseTree; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Set; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TreePermissionResp implements IBaseTree { + + /** + * 菜单树节点id + */ + private Long featureId; + + /** + * 菜单树节点名字 + */ + private String featureName; + + /** + * 菜单资源类型1-菜单 2-页面 3-应用入口 4-组件 5-root节点 6-分组 + */ + private Integer featureType; + + /** + * 跳转类型 1-站内跳转 2-站外跳转 + */ + private Integer redirectType; + + /** + * 资源跳转URI + */ + private String linkUrl; + + /** + * 菜单树节点对应的前端featureCodes + */ + private Set featureCodes; + + /** 图标 **/ + private String icon; + + /** + * 选中的图标 + */ + private String activeIcon; + + /** + * 更多图标 + */ + private String moreIcon; + + /** + * 上级资源ID + */ + private Long parentId; + + /** + * 资源状态 0-隐藏 1-展示 + */ + private Integer status; + + /** + * 菜单页面编码,端唯一 + */ + private String uniCode; + + /** + * 展示顺序 + */ + private Integer displayOrder; + + /** + * 菜单树子节点信息 + */ + private List children; + + @JsonIgnore + @Override + public Long getNodeCode() { + return this.getFeatureId(); + } + + @JsonIgnore + @Override + public Long getParentNodeCode() { + return this.getParentId(); + } + + @JsonIgnore + @Override + public List getNodeChildren() { + return this.getChildren(); + } + + @Override + public void setNodeChildren(List nodeChildren) { + this.children = nodeChildren; + } +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/WorkspaceProductResp.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/WorkspaceProductResp.java new file mode 100644 index 00000000..630fac9c --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/res/WorkspaceProductResp.java @@ -0,0 +1,123 @@ +package cn.axzo.tyr.client.model.res; + +import com.google.common.collect.Lists; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/4/28 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WorkspaceProductResp { + + /** + * 产品版本产品 + */ + private List productVersions; + + /** + * 增值服务产品 + */ + private List addValueServices; + + /** + * 通用服务产品 + */ + private List generalServices; + + /** + * 硬件服务产品 + */ + private List hardware; + + /** + * 政务产品 + */ + private List governmentProduct; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Product { + + /** + * 产品ID + */ + private Long productId; + + /** + * 产品名称 + */ + private String productName; + + /** + * 人数上限 <企业、项目产品> + */ + private Integer maxPersonCount; + + /** + * 最大项目数 <企业产品> + */ + private Integer maxWorkspaceCount; + + /** + * 版本升级序列(数字越小,版本越低,不能降级,只能升级) <企业、项目产品> + */ + private Integer version; + + /** + * SKU列表 + */ + private List skus; + } + + + + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Sku { + + /** + * SKU名称 + */ + private String skuName; + + /** + * 规格型号 + */ + private String model; + + /** + * 数量 + */ + private Integer count; + + /** + * 单位 + */ + private String unit; + } + + public static WorkspaceProductResp init() { + return WorkspaceProductResp.builder() + .productVersions(Lists.newArrayList()) + .addValueServices(Lists.newArrayList()) + .generalServices(Lists.newArrayList()) + .hardware(Lists.newArrayList()) + .governmentProduct(Lists.newArrayList()) + .build(); + } +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/RoleUserUpdateReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/RoleUserUpdateReq.java new file mode 100644 index 00000000..74254059 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/RoleUserUpdateReq.java @@ -0,0 +1,28 @@ +package cn.axzo.tyr.client.model.roleuser; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class RoleUserUpdateReq { + + @NotNull(message = "id不能为空") + @Min(value = 0L) + private Long id; + + @NotNull(message = "id不能为空") + @Min(value = 0L) + private Long identityId; + + @NotNull(message = "id不能为空") + @Min(value = 0L) + private Long naturalPersonId; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/dto/GetUserFeatureResourceIdsResp.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/dto/GetUserFeatureResourceIdsResp.java new file mode 100644 index 00000000..9523133e --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/dto/GetUserFeatureResourceIdsResp.java @@ -0,0 +1,25 @@ +package cn.axzo.tyr.client.model.roleuser.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Set; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/5/22 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class GetUserFeatureResourceIdsResp { + + /** + * 资源ID列表 + */ + private Set featureResourceIds; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/dto/SaasRoleUserDTO.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/dto/SaasRoleUserDTO.java index 336d11f0..4381bb56 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/dto/SaasRoleUserDTO.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/dto/SaasRoleUserDTO.java @@ -6,6 +6,8 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.util.Date; + /** * @author haiyangjin * @date 2023/9/14 @@ -94,4 +96,9 @@ public class SaasRoleUserDTO { * '是否启用' */ private Boolean enabled; + + /** + * 更新时间 + */ + private Date updateAt; } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/dto/SaasRoleUserV2DTO.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/dto/SaasRoleUserV2DTO.java new file mode 100644 index 00000000..d031d60a --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/dto/SaasRoleUserV2DTO.java @@ -0,0 +1,95 @@ +package cn.axzo.tyr.client.model.roleuser.dto; + +import cn.axzo.tyr.client.model.res.SaasPermissionRelationRes; +import cn.axzo.tyr.client.model.res.SaasPermissionRes; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class SaasRoleUserV2DTO { + + private Long id; + + /** + * 角色Id + */ + private Long roleId; + + private SaasRoleUser saasRoleUser; + + private SaasRole saasRole; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class SaasRoleUser { + + /** + * 用户id + */ + private Long personId; + + /** + * 用户名字 + */ + private String realName; + + /** + * 单位Id + */ + private Long ouId; + + /** + * 项目id + */ + private Long workspaceId; + + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class SaasRole { + private Long id; + + /** + * 角色名称 + */ + private String name; + + /** + * 角色类型: + * cn.axzo.tyr.client.common.enums.RoleTypeEnum + */ + private String roleType; + + private Long workspaceId; + + private Long ownerOuId; + + /** + * 产品单位类型 + * 1:总包 2:建设单位 3:监理单位 4:劳务分包 5:专业分包 6:OMS通用 7:企业通用 8:企业内班组 9:项目内班组 + */ + private Integer productUnitType; + + /** + * 角色权限 + */ + private List saasPermissions; + + /** + * 角色关联的权限点id信息,没有featureCode,直接查询的pgPermissionRelation + */ + private List permissionRelations; + } +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/req/GetUserFeatureResourceIdsReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/req/GetUserFeatureResourceIdsReq.java new file mode 100644 index 00000000..b6a3036e --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/req/GetUserFeatureResourceIdsReq.java @@ -0,0 +1,44 @@ +package cn.axzo.tyr.client.model.roleuser.req; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; +import java.util.List; + + +/** + * @author likunpeng + * @date 2024/4/2 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class GetUserFeatureResourceIdsReq { + + /** + * 工作台id + */ + @NotNull(message = "workspaceId不能为空") + private Long workspaceId; + + /** + * 单位id + */ + @NotNull(message = "ouId不能为空") + private Long ouId; + + /** + * 自然人id + */ + @NotNull(message = "personId不能为空") + private Long personId; + + /** + * 查询的角色类型列表 + */ + private List roleIds; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/req/ListRoleUserRelationParam.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/req/ListRoleUserRelationParam.java new file mode 100644 index 00000000..d901dd58 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/req/ListRoleUserRelationParam.java @@ -0,0 +1,114 @@ +package cn.axzo.tyr.client.model.roleuser.req; + +import cn.axzo.foundation.dao.support.wrapper.CriteriaField; +import cn.axzo.foundation.dao.support.wrapper.Operator; +import cn.axzo.tyr.client.common.enums.FeatureResourceType; +import cn.axzo.tyr.client.model.enums.IdentityType; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +@SuperBuilder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ListRoleUserRelationParam { + + @CriteriaField(field = "roleId", operator = Operator.IN) + private List roleIds; + + @CriteriaField(field = "naturalPersonId", operator = Operator.IN) + private List personIds; + + @CriteriaField(field = "ouId", operator = Operator.EQ) + private Long ouId; + + /** + * 身份类型 1:工人 2:从业人员 3:班组长 4:运营人员 5:政务人员 + */ + @CriteriaField(field = "identityType", operator = Operator.EQ) + private IdentityType identityType; + + @CriteriaField(ignore = true) + private Boolean needUsers; + + /** + * 自然人Id + */ + @CriteriaField(field = "naturalPersonId", operator = Operator.EQ) + private Long personId; + + /** + * 身份Id + */ + @CriteriaField(field = "identityId", operator = Operator.EQ) + private Long identityId; + + /** + * workspaceId和ouId配对查询 + * 例如:((workspaceId = ## and ouId = ##) or (workspaceId = ## and ouId = ##)) + */ + @CriteriaField(ignore = true) + private List workspaceOuPairs; + + @CriteriaField(ignore = true) + private Boolean needRole; + + /** + * 从saas_feature中查询权限点 + */ + @CriteriaField(ignore = true) + private Boolean needRolePermissionOld; + + /** + * 根据权限点id过滤 + */ + @CriteriaField(ignore = true) + private List featureIds; + + @CriteriaField(ignore = true) + private Boolean needPermissionRelation; + + /** + * 查询菜单树节点类型 + */ + @CriteriaField(ignore = true) + private List featureResourceTypes; + + /** + * 新旧权限点,needPermissionRelation = true时最好带上,因为新旧权限点会有冲突的情况发生 + */ + @CriteriaField(ignore = true) + private Integer type; + + @CriteriaField(ignore = true) + private String terminal; + + /** + * 权限点从saas_feature_resource表查询 + */ + @CriteriaField(ignore = true) + private Boolean needPermission; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class WorkspaceOuPair { + + /** + * 租户id + */ + private Long workspaceId; + + /** + * 单位id + */ + private Long ouId; + } + +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/req/PageRoleUserRelationParam.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/req/PageRoleUserRelationParam.java new file mode 100644 index 00000000..01a2af54 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/req/PageRoleUserRelationParam.java @@ -0,0 +1,29 @@ +package cn.axzo.tyr.client.model.roleuser.req; + +import cn.axzo.foundation.dao.support.wrapper.CriteriaField; +import cn.axzo.foundation.page.IPageReq; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +@SuperBuilder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class PageRoleUserRelationParam extends ListRoleUserRelationParam implements IPageReq { + + @CriteriaField(ignore = true) + Integer page; + + @CriteriaField(ignore = true) + Integer pageSize; + + /** + * 排序:使用示例,createTime__DESC + */ + @CriteriaField(ignore = true) + List sort; +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/req/RoleUserReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/req/RoleUserReq.java index d4f77a3a..9356c65c 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/req/RoleUserReq.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/roleuser/req/RoleUserReq.java @@ -69,6 +69,10 @@ public class RoleUserReq { */ private boolean isRecycleModel; + /** + * 操作人 + */ + private Long operator; /** * 更新时是否覆盖特殊角色 * 这些角色id是在nacos上的配置,在cms的单位管理-通讯录不会回显该角色 diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/vo/Operator.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/vo/Operator.java new file mode 100644 index 00000000..ca039c7f --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/vo/Operator.java @@ -0,0 +1,42 @@ +package cn.axzo.tyr.client.model.vo; + +import cn.axzo.framework.auth.domain.ContextInfo; +import cn.axzo.framework.auth.domain.ContextInfoHolder; +import cn.axzo.framework.auth.domain.UserInfo; +import lombok.Data; + +/** + * @author yanglin + */ +@Data +public class Operator { + + private String realName; + private Long personId; + + /** + * For a gateway like yoke + */ + public static Operator fromAuthContext() { + ContextInfo ctx = ContextInfoHolder.get(); + if (ctx == null) { + return absent(); + } + UserInfo userInfo = ctx.getUserInfo(); + if (userInfo == null) { + return absent(); + } + Operator operator = new Operator(); + operator.setRealName(userInfo.getRealName()); + operator.setPersonId(userInfo.getPersonId()); + return operator; + } + + public static Operator absent() { + Operator operator = new Operator(); + operator.setRealName(""); + operator.setPersonId(0L); + return operator; + } + +} \ No newline at end of file diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/vo/OperatorRequest.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/vo/OperatorRequest.java new file mode 100644 index 00000000..55667726 --- /dev/null +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/vo/OperatorRequest.java @@ -0,0 +1,18 @@ +package cn.axzo.tyr.client.model.vo; + +import lombok.Getter; +import lombok.Setter; + +/** + * @author yanglin + */ +@Setter +@Getter +public class OperatorRequest { + + private Operator operator; + + public Operator determineOperator() { + return operator == null ? Operator.absent() : operator; + } +} diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/vo/SaasRoleGroupVO.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/vo/SaasRoleGroupVO.java index 65340683..87f1152e 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/vo/SaasRoleGroupVO.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/vo/SaasRoleGroupVO.java @@ -70,4 +70,10 @@ public class SaasRoleGroupVO { * 上级分组id */ private Long parentId; + + private Operator operator; + + public Operator determineOperator() { + return operator == null ? Operator.absent() : operator; + } } diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/vo/SaveOrUpdatePermissionGroupVO.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/vo/SaveOrUpdatePermissionGroupVO.java index de5f234e..7fd65978 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/vo/SaveOrUpdatePermissionGroupVO.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/vo/SaveOrUpdatePermissionGroupVO.java @@ -34,7 +34,7 @@ public class SaveOrUpdatePermissionGroupVO { private String type; /** - * 已选择的项目部 + * 已选择的项目 */ @Valid private List selectedWorkspace; diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/vo/SaveOrUpdateRoleVO.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/vo/SaveOrUpdateRoleVO.java index f51a165f..0e53d2d7 100644 --- a/tyr-api/src/main/java/cn/axzo/tyr/client/model/vo/SaveOrUpdateRoleVO.java +++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/vo/SaveOrUpdateRoleVO.java @@ -99,7 +99,7 @@ public class SaveOrUpdateRoleVO { private Long id; /** - * 项目部类型字典code + * 项目类型字典code */ private String workspaceTypeCode; } diff --git a/tyr-server/pom.xml b/tyr-server/pom.xml index f0f5014b..ac8c62dd 100644 --- a/tyr-server/pom.xml +++ b/tyr-server/pom.xml @@ -103,8 +103,9 @@ - cn.axzo.pokonyan - pokonyan + cn.axzo.foundation + dao-support-lib + 2.0.0-SNAPSHOT @@ -122,6 +123,21 @@ pom + + cn.axzo.foundation + common-lib + 2.0.0-SNAPSHOT + + + cn.axzo.basics + basics-profiles-api + + + + cn.axzo.platform + apisix-plat-api + 2.0.0-SNAPSHOT + diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/TyrApplication.java b/tyr-server/src/main/java/cn/axzo/tyr/server/TyrApplication.java index 98fed725..1555d7f2 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/TyrApplication.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/TyrApplication.java @@ -1,6 +1,7 @@ package cn.axzo.tyr.server; import cn.axzo.pokonyan.config.mybatisplus.EntityMetaObjectHandler; +import cn.axzo.tyr.server.config.RocketMQEventConfiguration; import cn.axzo.tyr.server.job.CMSRoleJobHandler; import cn.hutool.extra.spring.SpringUtil; import lombok.extern.slf4j.Slf4j; @@ -20,6 +21,7 @@ import org.springframework.scheduling.annotation.EnableAsync; @EnableDiscoveryClient @MapperScan(value = {"cn.axzo.tyr.server.repository.mapper"}) @SpringBootApplication(scanBasePackages = "cn.axzo") +@Import(RocketMQEventConfiguration.class) public class TyrApplication { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(TyrApplication.class, args); diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/TyrServerDevApplication.java b/tyr-server/src/main/java/cn/axzo/tyr/server/TyrServerDevApplication.java index e91e7766..b15ee22a 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/TyrServerDevApplication.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/TyrServerDevApplication.java @@ -1,10 +1,12 @@ package cn.axzo.tyr.server; +import cn.axzo.tyr.server.config.RocketMQEventConfiguration; import lombok.extern.log4j.Log4j2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Import; import org.springframework.core.env.Environment; import org.springframework.scheduling.annotation.EnableAsync; @@ -15,6 +17,7 @@ import org.springframework.scheduling.annotation.EnableAsync; @EnableFeignClients(basePackages = {"cn.axzo"}) @Log4j2 @EnableAsync +@Import(RocketMQEventConfiguration.class) public class TyrServerDevApplication { public static void main(String[] args) throws Exception { diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/TyrServerPreApplication.java b/tyr-server/src/main/java/cn/axzo/tyr/server/TyrServerPreApplication.java index 08894d19..9d871bb8 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/TyrServerPreApplication.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/TyrServerPreApplication.java @@ -1,10 +1,12 @@ package cn.axzo.tyr.server; +import cn.axzo.tyr.server.config.RocketMQEventConfiguration; import lombok.extern.log4j.Log4j2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Import; import org.springframework.core.env.Environment; import org.springframework.scheduling.annotation.EnableAsync; @@ -16,6 +18,7 @@ import org.springframework.scheduling.annotation.EnableAsync; @EnableFeignClients(basePackages = {"cn.axzo"}) @Log4j2 @EnableAsync +@Import(RocketMQEventConfiguration.class) public class TyrServerPreApplication { public static void main(String[] args) throws Exception { @@ -28,6 +31,8 @@ public class TyrServerPreApplication { System.setProperty("spring.redis.port", "6379"); System.setProperty("spring.redis.host", "172.16.1.76"); + System.setProperty("rocketmq.name-server", "172.16.2.82:9876"); + SpringApplication application = new SpringApplication(TyrServerPreApplication.class); ApplicationContext applicationContext = application.run(args); Environment env = applicationContext.getEnvironment(); diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/TyrServerTestApplication.java b/tyr-server/src/main/java/cn/axzo/tyr/server/TyrServerTestApplication.java index 39e72df2..30bc2957 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/TyrServerTestApplication.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/TyrServerTestApplication.java @@ -1,10 +1,12 @@ package cn.axzo.tyr.server; +import cn.axzo.tyr.server.config.RocketMQEventConfiguration; import lombok.extern.log4j.Log4j2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Import; import org.springframework.core.env.Environment; import org.springframework.scheduling.annotation.EnableAsync; @@ -16,6 +18,7 @@ import org.springframework.scheduling.annotation.EnableAsync; @EnableFeignClients(basePackages = {"cn.axzo"}) @Log4j2 @EnableAsync +@Import(RocketMQEventConfiguration.class) public class TyrServerTestApplication { public static void main(String[] args) throws Exception { diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/config/ContextInfoFillInterceptor.java b/tyr-server/src/main/java/cn/axzo/tyr/server/config/ContextInfoFillInterceptor.java new file mode 100644 index 00000000..1af78d4a --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/config/ContextInfoFillInterceptor.java @@ -0,0 +1,76 @@ +package cn.axzo.tyr.server.config; + +import cn.axzo.framework.auth.config.FeignContextInfoInterceptor; +import cn.axzo.framework.auth.domain.*; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Objects; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2024/6/19 12:44 + */ +@Slf4j +@Component +public class ContextInfoFillInterceptor implements HandlerInterceptor { + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { + HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { + ContextInfoHolder.clear(); + HandlerInterceptor.super.afterCompletion(request, response, handler, ex); + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + String lite = request.getHeader(FeignContextInfoInterceptor.LITE); + if (StrUtil.isNotBlank(lite)) { + try { + ContextInfo.LiteSaasContext liteSaasContext = JSONUtil.toBean(lite, ContextInfo.LiteSaasContext.class); + String serverName = request.getHeader(FeignContextInfoInterceptor.SERVICE_NAME); + if (Objects.nonNull(liteSaasContext)) { + ContextInfo contextInfo = new ContextInfo(); + contextInfo.setWorkspaceId(liteSaasContext.getWorkspaceId()); + contextInfo.setWorkspaceType(liteSaasContext.getWorkspaceType()); + contextInfo.setOuId(liteSaasContext.getOuId()); + contextInfo.setOuType(liteSaasContext.getOuType()); + contextInfo.setSaasTenantId(liteSaasContext.getSaasTennantId()); + + UserInfo userInfo = new UserInfo(); + userInfo.setIdentityId(liteSaasContext.getIdentityId()); + userInfo.setIdentityType(liteSaasContext.getIdentityType()); + userInfo.setPersonId(liteSaasContext.getPersonId()); + userInfo.setAcntId(liteSaasContext.getAcntId()); + userInfo.setRealName(liteSaasContext.getRealName()); + contextInfo.setUserInfo(userInfo); + + TerminalInfo terminalInfo = new TerminalInfo(liteSaasContext.getTerminal()); + contextInfo.setTerminalInfo(terminalInfo); + + SystemAndDeviceInfo systemAndDeviceInfo = new SystemAndDeviceInfo(); + systemAndDeviceInfo.setIpAddress(liteSaasContext.getIpAddress()); + contextInfo.setSystemAndDeviceInfo(systemAndDeviceInfo); + + ContextInfoHolder.set(contextInfo); + log.info("ContextInfo parsing success! server: {}, contextInfo: {}", StrUtil.isNotBlank(serverName) ? serverName : "unknown", lite); + } + } catch (Exception e) { + log.warn("ContextInfo parsing failure! {}", e.getMessage()); + } + return true; + } + log.warn("Can not get LiteSaasContext!"); + return true; + } +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/config/FeignConfig.java b/tyr-server/src/main/java/cn/axzo/tyr/server/config/FeignConfig.java index 4a3e4ed1..0f71602f 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/config/FeignConfig.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/config/FeignConfig.java @@ -35,9 +35,13 @@ public class FeignConfig implements RequestInterceptor, EnvironmentAware { @Value("${maokaiEnvUrl:http://dev-app.axzo.cn/maokai}") private String maokaiEnvUrl; + @Value("${pudgeEnvUrl:http://dev-app.axzo.cn/pudge}") + private String pudgeEnvUrl; + + @Value("${apisixUrl:http://dev-app.axzo.cn/apisix-plat}") + private String apisixUrl; private static String POD_NAMESPACE; - static { Map env = System.getenv(); if (env != null) { @@ -57,6 +61,9 @@ public class FeignConfig implements RequestInterceptor, EnvironmentAware { url = url.replace("http://workflow-engine:8080", workflowEnvUrl); url = url.replace("http://thrones", thronesEnvUrl); url = url.replace("http://maokai:8080", maokaiEnvUrl); + url = url.replace("http://pudge:10099", pudgeEnvUrl); + url = url.replace("http://apisix-plat:8080", apisixUrl); + String profile = environment.getProperty("spring.profiles.active"); if(Objects.equals(profile, "test") && url.contains("dev-app.axzo.cn")) { url = url.replace("dev-app", "test-api"); diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/config/GlobalConfig.java b/tyr-server/src/main/java/cn/axzo/tyr/server/config/GlobalConfig.java index a9aadbde..c70befba 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/config/GlobalConfig.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/config/GlobalConfig.java @@ -18,7 +18,8 @@ import static cn.axzo.tyr.server.config.GlobalConfig.FeignClientConstant.*; @Configuration @EnableFeignClients(basePackages = { WORKFLOW_ENGINE, - INNER_FEIGN + INNER_FEIGN, + "cn.axzo" }) public class GlobalConfig { /** diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/config/RocketMQEventConfiguration.java b/tyr-server/src/main/java/cn/axzo/tyr/server/config/RocketMQEventConfiguration.java new file mode 100644 index 00000000..7b7b391f --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/config/RocketMQEventConfiguration.java @@ -0,0 +1,90 @@ +package cn.axzo.tyr.server.config; + +import cn.axzo.framework.rocketmq.BaseListener; +import cn.axzo.framework.rocketmq.DefaultEventConsumer; +import cn.axzo.framework.rocketmq.EventConsumer; +import cn.axzo.framework.rocketmq.EventHandlerRepository; +import cn.axzo.framework.rocketmq.EventProducer; +import cn.axzo.framework.rocketmq.RocketMQEventProducer; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.spring.annotation.ConsumeMode; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +import java.util.function.Consumer; + +/** + * @Author: liyong.tian + * @Date: 2023/7/25 14:43 + * @Description: + */ +@Slf4j +public class RocketMQEventConfiguration { + + @Value("${spring.application.name}") + private String appName; + + @Value("${topic}") + private String topic; + + @Bean + public RocketMQTemplate ser(){ + return new RocketMQTemplate(); + } + @Bean + EventProducer eventProducer(RocketMQTemplate rocketMQTemplate) { + return new RocketMQEventProducer(rocketMQTemplate, + appName, + appName, + EventProducer.Context.builder() + .meta(RocketMQEventProducer.RocketMQMessageMeta.builder() + .topic(topic) + .build()) + .build(), + null + ); + } + + @Bean + EventConsumer eventConsumer(EventHandlerRepository eventHandlerRepository) { + Consumer callback = (eventWrapper) -> { + if (eventWrapper.isHandled()) { + // 只收集被App真正消费的消息. + //String topic = (String) eventWrapper.getExt().get(EVENT_TOPIC_KEY); + + } + }; + return new DefaultEventConsumer(appName, eventHandlerRepository, callback); + } + + @Slf4j + @Component + @RocketMQMessageListener(topic = "topic_thrones_${spring.profiles.active}", + consumerGroup = "GID_topic_thrones_${spring.application.name}_${spring.profiles.active}", + consumeMode = ConsumeMode.ORDERLY, + nameServer = "${rocketmq.name-server}" + ) + public static class DefaultListener extends BaseListener implements RocketMQListener { + + @Autowired + private EventConsumer eventConsumer; + + @Override + public void onMessage(MessageExt message) { + super.onEvent(message, eventConsumer); + } + } + + @Bean + EventHandlerRepository eventHandlerRepository() { + return new EventHandlerRepository((ex, logText) -> { + log.warn("MQ, handle warning {}", logText, ex); + }); + } +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/config/WebConfig.java b/tyr-server/src/main/java/cn/axzo/tyr/server/config/WebConfig.java new file mode 100644 index 00000000..731af555 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/config/WebConfig.java @@ -0,0 +1,25 @@ +package cn.axzo.tyr.server.config; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * @author chenwenjian + * @version 1.0 + * @date 2024/6/19 13:17 + */ +@Configuration +@RequiredArgsConstructor +public class WebConfig implements WebMvcConfigurer { + + private final ContextInfoFillInterceptor contextInfoFillInterceptor; + + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(contextInfoFillInterceptor) + .addPathPatterns("/**"); + } + + +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/config/exception/BizResultCode.java b/tyr-server/src/main/java/cn/axzo/tyr/server/config/exception/BizResultCode.java new file mode 100644 index 00000000..656ea649 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/config/exception/BizResultCode.java @@ -0,0 +1,20 @@ +package cn.axzo.tyr.server.config.exception; + +import cn.axzo.foundation.result.IResultCode; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum BizResultCode implements IResultCode { + + CANT_DELETE_ROLE_GROUP("100001", "不能删除角色分组,当前角色分组下有子角色分组"), + ROLE_GROUP_NOT_FOUND("100002", "角色分组不存在"), + REDIS_ROLE_NOT_NULL("100003", "角色id不能为空"), + REDIS_PRODUCT_NOT_NULL("100004", "产品不能为空"), + FEATURE_RESOURCE_NOT_FOUND("100005", "菜单资源不存在"), + WORKSPACE_ID_NOT_NULL("100006", "项目id不能为空"); + + private String errorCode; + private String errorMessage; +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/config/exception/ExceptionAdviceHandler.java b/tyr-server/src/main/java/cn/axzo/tyr/server/config/exception/ExceptionAdviceHandler.java index e4a10811..e82c9e87 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/config/exception/ExceptionAdviceHandler.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/config/exception/ExceptionAdviceHandler.java @@ -1,7 +1,8 @@ package cn.axzo.tyr.server.config.exception; -import cn.axzo.basics.common.exception.ServiceException; +import cn.axzo.foundation.exception.BusinessException; import cn.axzo.framework.domain.web.result.ApiResult; +import cn.azxo.framework.common.model.CommonResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.core.annotation.Order; import org.springframework.util.CollectionUtils; @@ -48,4 +49,9 @@ public class ExceptionAdviceHandler { return ApiResult.err(objectErrorDefaultMessage); } + @ExceptionHandler(BusinessException.class) + public CommonResponse businessExceptionHandler(BusinessException e) { + log.warn("参数错误", e); + return CommonResponse.error(Integer.valueOf(e.getErrorCode()), e.getErrorMsg()); + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/PrivateController.java b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/PrivateController.java index add17cc0..7a042be8 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/PrivateController.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/PrivateController.java @@ -1,33 +1,88 @@ package cn.axzo.tyr.server.controller; +import cn.axzo.basics.common.constant.enums.DeleteEnum; +import cn.axzo.basics.common.constant.enums.TableIsDeleteEnum; +import cn.axzo.basics.common.util.TreeUtil; +import cn.axzo.foundation.page.PageResp; +import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; import cn.axzo.tyr.client.model.req.CommonDictQueryReq; +import cn.axzo.tyr.client.model.req.GetFeatureResourceTreeReq; +import cn.axzo.tyr.client.model.req.PagePgroupPermissionRelationReq; +import cn.axzo.tyr.client.model.req.PageProductFeatureRelationReq; +import cn.axzo.tyr.client.model.req.PermissionCheckReq; import cn.axzo.tyr.client.model.req.QuerySaasRoleGroupReq; import cn.axzo.tyr.client.model.res.CommonDictResp; +import cn.axzo.tyr.client.model.res.FeatureResourceDTO; +import cn.axzo.tyr.client.model.res.FeatureResourceTreeNode; import cn.axzo.tyr.client.model.res.SaasRoleRes; import cn.axzo.tyr.client.model.vo.SaasRoleGroupVO; +import cn.axzo.tyr.client.model.vo.SaveOrUpdateRoleVO; +import cn.axzo.tyr.server.event.outer.CacheWorkspaceProductHandler; +import cn.axzo.tyr.server.event.payload.ServicePkgProductCreatedPayload; +import cn.axzo.tyr.server.repository.dao.SaasFeatureDao; +import cn.axzo.tyr.server.repository.dao.SaasFeatureResourceDao; +import cn.axzo.tyr.server.repository.dao.SaasPgroupPermissionRelationDao; +import cn.axzo.tyr.server.repository.dao.SaasPgroupRoleRelationDao; +import cn.axzo.tyr.server.repository.dao.SaasRoleDao; +import cn.axzo.tyr.server.repository.dao.SaasRoleGroupDao; import cn.axzo.tyr.server.repository.dao.SaasRoleGroupRelationDao; +import cn.axzo.tyr.server.repository.entity.SaasFeature; +import cn.axzo.tyr.server.repository.entity.SaasFeatureResource; +import cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation; +import cn.axzo.tyr.server.repository.entity.SaasPgroupRoleRelation; +import cn.axzo.tyr.server.repository.entity.SaasProductModuleFeatureRelation; import cn.axzo.tyr.server.repository.entity.SaasRole; import cn.axzo.tyr.server.repository.entity.SaasRoleGroup; import cn.axzo.tyr.server.repository.entity.SaasRoleGroupRelation; +import cn.axzo.tyr.server.service.ProductFeatureRelationService; +import cn.axzo.tyr.server.service.ProductPermissionCacheService; import cn.axzo.tyr.server.service.RoleService; import cn.axzo.tyr.server.service.SaasCommonDictService; +import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationService; import cn.axzo.tyr.server.service.SaasRoleGroupService; +import cn.axzo.tyr.server.service.TyrSaasAuthService; +import cn.axzo.tyr.server.service.WorkspaceProductService; +import cn.axzo.tyr.server.service.impl.SaasFeatureResourceCacheService; +import cn.axzo.tyr.server.util.FeatureCodeUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.fastjson.JSON; import com.google.common.collect.Lists; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.IOException; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; +import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.NEW_FEATURE; +import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.OLD_FEATURE; + +@Slf4j @RestController @RequiredArgsConstructor public class PrivateController { @@ -40,6 +95,34 @@ public class PrivateController { private SaasRoleGroupRelationDao saasRoleGroupRelationDao; @Autowired private RoleService roleService; + @Autowired + private SaasFeatureResourceDao saasFeatureResourceDao; + @Autowired + private SaasFeatureResourceCacheService saasFeatureResourceCacheService; + @Autowired + private ProductPermissionCacheService productPermissionCacheService; + @Autowired + private TyrSaasAuthService tyrSaasAuthService; + @Autowired + private SaasFeatureDao saasFeatureDao; + @Autowired + private SaasPgroupPermissionRelationService saasPgroupPermissionRelationService; + @Autowired + private SaasRoleDao saasRoleDao; + @Autowired + private SaasPgroupRoleRelationDao saasPgroupRoleRelationDao; + @Autowired + private SaasPgroupPermissionRelationDao saasPgroupPermissionRelationDao; + @Autowired + private ProductFeatureRelationService productFeatureRelationService; + @Autowired + private FeatureCodeUtil featureCodeUtil; + @Autowired + private WorkspaceProductService workspaceProductService; + @Autowired + private CacheWorkspaceProductHandler cacheWorkspaceProductHandler; + @Autowired + private SaasRoleGroupDao saasRoleGroupDao; /** * 统一层级的roleGroup按照id升序,sort从1递增 @@ -148,4 +231,495 @@ public class PrivateController { roleService.updateBatchById(updateSaasRoles); return "ok"; } + + @PostMapping("/api/private/saasFeatureResource/path/refresh") + public Object createFeatureResourceRoot(@Validated @RequestBody RefreshFeatureResourcePathParam request) { + GetFeatureResourceTreeReq getFeatureResourceTreeReq = GetFeatureResourceTreeReq.builder() + .terminals(Lists.newArrayList(request.getTerminal())) + .build(); + List saasFeatureResources = saasFeatureResourceCacheService.getByResourceTreeParam(getFeatureResourceTreeReq); + if (CollectionUtils.isEmpty(saasFeatureResources)) { + return Collections.emptyList(); + } + + List tree = TreeUtil.buildTree(saasFeatureResources.stream() + .map(featureResource -> FeatureResourceTreeNode.builder() + .id(featureResource.getId()) + .featureCode(featureResource.getFeatureCode()) + .featureName(featureResource.getFeatureName()) + .featureType(featureResource.getFeatureType()) + .terminal(featureResource.getTerminal()) + .parentId(featureResource.getParentId()) + .displayOrder(featureResource.getDisplayOrder()) + .build()) + .sorted(Comparator.comparing(FeatureResourceDTO::getDisplayOrder)) + .collect(Collectors.toList())); + + if (CollectionUtils.isEmpty(tree)) { + log.info("not found featureResource,{}", request.getTerminal()); + return "error"; + } + FeatureResourceTreeNode root = tree.stream() + .filter(e -> Objects.equals(e.getId(), request.getRootId())) + .findFirst() + .orElse(null); + if (root == null) { + log.info("not found root,{}", request.getTerminal()); + return "error"; + } + + SaasFeatureResource saasFeatureResource = new SaasFeatureResource(); + saasFeatureResource.setId(root.getId()); + saasFeatureResource.setParentId(0L); + saasFeatureResource.setPath(root.getId() + ","); + saasFeatureResourceDao.updateById(saasFeatureResource); + + updateChildrenPath(root, tree); + + saasFeatureResourceCacheService.clearCache(); + return "ok"; + } + + private void updateChildrenPath(FeatureResourceTreeNode parent, + List featureResources) { + + if (CollectionUtils.isEmpty(featureResources)) { + return; + } + + List children = featureResources.stream() + .filter(e -> !Objects.equals(e.getId(), parent.getId())) + .collect(Collectors.toList()); + + if (CollectionUtils.isEmpty(children)) { + return; + } + SaasFeatureResource parentFeatureResource = saasFeatureResourceDao.getById(parent.getId()); + + children.forEach(e -> { + SaasFeatureResource saasFeatureResource = new SaasFeatureResource(); + saasFeatureResource.setId(e.getId()); + saasFeatureResource.setParentId(parentFeatureResource.getId()); + saasFeatureResource.setPath(parentFeatureResource.getPath() + e.getId() + ","); + saasFeatureResourceDao.updateById(saasFeatureResource); + updateChildrenPath(e, e.getChildren()); + }); + } + + @PostMapping("/api/private/productPermission/add") + public Object addProductPermission(@Validated @RequestBody ProductPermissionCacheService.StoreProductPermissionParam request) { + productPermissionCacheService.store(request); + return "ok"; + } + + @PostMapping("/api/private/productPermission/get") + public Object getProductPermission(@Validated @RequestBody ProductPermissionCacheService.ListProductPermissionParam request) { + return productPermissionCacheService.list(request); + } + + @PostMapping("/api/private/productPermission/has") + public Object hasProductIds(@Validated @RequestBody ProductPermissionCacheService.HasProductPermissionParam request) { + return productPermissionCacheService.hasProductIds(request); + } + + @PostMapping("/api/private/permission/auth") + public Object authPermission(@Validated @RequestBody PermissionCheckReq request) { + return tyrSaasAuthService.authPermission(request); + } + + /** 刷新表saas_pgroup_permission_relation的type字段 **/ + @PostMapping("/api/private/pgroupPermissionRelation/refresh") + public ApiResult refreshPgroupPermissionRelation(@Validated @RequestBody RefreshPgroupPermissionRelationParam request) { + List workspaceTypes = Lists.newArrayList(3,6); + if (!workspaceTypes.contains(request.getWorkspaceType())) { + return ApiResult.err("非oms、政务的角色,不能清洗。"); + } + + Long startRoleId = 0L; + while (true) { + List saasRoles = saasRoleDao.lambdaQuery() + .eq(BaseEntity::getIsDelete, DeleteEnum.NORMAL.getValue()) + .eq(SaasRole::getWorkspaceType, request.getWorkspaceType()) + .gt(BaseEntity::getId, startRoleId) + .orderByAsc(BaseEntity::getId) + .last("LIMIT " + request.getPageSize()) + .list(); + if (CollectionUtils.isEmpty(saasRoles)) { + return ApiResult.ok(); + } + processRole(saasRoles.stream().map(BaseEntity::getId).collect(Collectors.toList())); + + startRoleId = saasRoles.get(saasRoles.size() - 1).getId(); + } + } + + private void processRole(List roleIds) { + List saasPgroupRoleRelations = saasPgroupRoleRelationDao.findByRoleIds(roleIds); + if (CollectionUtils.isEmpty(saasPgroupRoleRelations)) { + log.info("refreshPgroupPermissionRelation roleIds:{} has no pgroup role relation", JSON.toJSONString(roleIds)); + return; + } + List groupIds = saasPgroupRoleRelations.stream().map(SaasPgroupRoleRelation::getGroupId).filter(Objects::nonNull).distinct().collect(Collectors.toList()); + if (CollectionUtils.isEmpty(groupIds)) { + return; + } + saasPgroupPermissionRelationDao.lambdaUpdate() + .eq(BaseEntity::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .in(SaasPgroupPermissionRelation::getGroupId, groupIds) + .set(SaasPgroupPermissionRelation::getType, 1) + .update(); + } + + /** + * 刷新saas_pgroup_permission_relation表的featureType + * 1、type = 0的数据从saas_feature中查询 + * 2、type = 1的数据从saas_feature_resource中查询 + * @param request + * @return + */ + @PostMapping("/api/private/pGroupPermissionRelation/featureResource/refresh") + public Object reFreshFeature(@Validated @RequestBody RefreshPGroupPermissionFeatureParam request) { + + final Integer DEFAULT_PAGE_SIZE = 100; + Integer pageNumber = 1; + while (true) { + PagePgroupPermissionRelationReq req = PagePgroupPermissionRelationReq.builder() + .ids(request.getIds()) + .page(pageNumber++) + .pageSize(DEFAULT_PAGE_SIZE) + .build(); + + PageResp page = saasPgroupPermissionRelationService.page(req); + if (CollectionUtils.isNotEmpty(page.getData())) { + updateOldRoleFeature(page.getData()); + + updateNewRoleFeature(page.getData()); + } + + if (!page.hasNext()) { + break; + } + } + return "ok"; + } + + private void updateNewRoleFeature(List saasPgroupPermissionRelations) { + List newFeatureRelations = saasPgroupPermissionRelations.stream() + .filter(e -> Objects.equals(e.getType(), NEW_FEATURE)) + .collect(Collectors.toList()); + + if (CollectionUtils.isEmpty(newFeatureRelations)) { + return; + } + + Map saasFeatureResourceMap = saasFeatureResourceDao.listByIds(Lists.transform(newFeatureRelations, SaasPgroupPermissionRelation::getFeatureId)) + .stream() + .collect(Collectors.toMap(SaasFeatureResource::getId, Function.identity())); + + List update = newFeatureRelations.stream() + .map(e -> { + SaasFeatureResource saasFeatureResource = saasFeatureResourceMap.get(e.getFeatureId()); + if (saasFeatureResource == null) { + return null; + } + + SaasPgroupPermissionRelation saasPgroupPermissionRelation = new SaasPgroupPermissionRelation(); + saasPgroupPermissionRelation.setId(e.getId()); + saasPgroupPermissionRelation.setFeatureType(saasFeatureResource.getFeatureType()); + return saasPgroupPermissionRelation; + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (CollectionUtils.isEmpty(update)) { + return; + } + + saasPgroupPermissionRelationService.updateBatchById(update); + } + + private void updateOldRoleFeature(List saasPgroupPermissionRelations) { + List oldFeatureRelations = saasPgroupPermissionRelations.stream() + .filter(e -> Objects.equals(e.getType(), OLD_FEATURE)) + .collect(Collectors.toList()); + + if (CollectionUtils.isEmpty(oldFeatureRelations)) { + return; + } + + Map saasFeatureMap = saasFeatureDao.listByIds(Lists.transform(oldFeatureRelations, SaasPgroupPermissionRelation::getFeatureId)) + .stream() + .collect(Collectors.toMap(SaasFeature::getId, Function.identity())); + + List update = oldFeatureRelations.stream() + .map(e -> { + SaasFeature saasFeature = saasFeatureMap.get(e.getFeatureId()); + if (saasFeature == null) { + return null; + } + + SaasPgroupPermissionRelation saasPgroupPermissionRelation = new SaasPgroupPermissionRelation(); + saasPgroupPermissionRelation.setId(e.getId()); + saasPgroupPermissionRelation.setFeatureType(saasFeature.getFeatureType()); + return saasPgroupPermissionRelation; + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (CollectionUtils.isEmpty(update)) { + return; + } + + saasPgroupPermissionRelationService.updateBatchById(update); + } + + /** + * 刷新saas_product_module_feature_relation表的featureType + * 1、type = 0的数据从saas_feature中查询 + * 2、type = 1的数据从saas_feature_resource中查询 + * @param request + * @return + */ + @PostMapping("/api/private/ProductPermissionRelation/featureResource/refresh") + public Object reFreshProductFeature(@Validated @RequestBody RefreshProductPermissionFeatureParam request) { + + final Integer DEFAULT_PAGE_SIZE = 100; + Integer pageNumber = 1; + while (true) { + PageProductFeatureRelationReq req = PageProductFeatureRelationReq.builder() + .ids(request.getIds()) + .page(pageNumber++) + .pageSize(DEFAULT_PAGE_SIZE) + .build(); + + PageResp page = productFeatureRelationService.page(req); + if (CollectionUtils.isNotEmpty(page.getData())) { + updateOldProductFeature(page.getData()); + + updateNewProductFeature(page.getData()); + } + + if (!page.hasNext()) { + break; + } + } + return "ok"; + } + + private void updateNewProductFeature(List saasProductModuleFeatureRelations) { + List newFeatureRelations = saasProductModuleFeatureRelations.stream() + .filter(e -> Objects.equals(e.getType(), NEW_FEATURE)) + .collect(Collectors.toList()); + + if (CollectionUtils.isEmpty(newFeatureRelations)) { + return; + } + + Map saasFeatureResourceMap = saasFeatureResourceDao.listByIds(Lists.transform(newFeatureRelations, SaasProductModuleFeatureRelation::getFeatureId)) + .stream() + .collect(Collectors.toMap(SaasFeatureResource::getId, Function.identity())); + + List update = newFeatureRelations.stream() + .map(e -> { + SaasFeatureResource saasFeatureResource = saasFeatureResourceMap.get(e.getFeatureId()); + if (saasFeatureResource == null) { + return null; + } + + SaasProductModuleFeatureRelation saasProductModuleFeatureRelation = new SaasProductModuleFeatureRelation(); + saasProductModuleFeatureRelation.setId(e.getId()); + saasProductModuleFeatureRelation.setFeatureType(saasFeatureResource.getFeatureType()); + return saasProductModuleFeatureRelation; + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (CollectionUtils.isEmpty(update)) { + return; + } + + productFeatureRelationService.updateBatchById(update); + } + + private void updateOldProductFeature(List saasProductModuleFeatureRelations) { + List oldFeatureRelations = saasProductModuleFeatureRelations.stream() + .filter(e -> Objects.equals(e.getType(), OLD_FEATURE)) + .collect(Collectors.toList()); + + if (CollectionUtils.isEmpty(oldFeatureRelations)) { + return; + } + + Map saasFeatureMap = saasFeatureDao.listByIds(Lists.transform(oldFeatureRelations, SaasProductModuleFeatureRelation::getFeatureId)) + .stream() + .collect(Collectors.toMap(SaasFeature::getId, Function.identity())); + + List update = oldFeatureRelations.stream() + .map(e -> { + SaasFeature saasFeature = saasFeatureMap.get(e.getFeatureId()); + if (saasFeature == null) { + return null; + } + + SaasProductModuleFeatureRelation saasProductModuleFeatureRelation = new SaasProductModuleFeatureRelation(); + saasProductModuleFeatureRelation.setId(e.getId()); + saasProductModuleFeatureRelation.setFeatureType(saasFeature.getFeatureType()); + return saasProductModuleFeatureRelation; + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (CollectionUtils.isEmpty(update)) { + return; + } + + productFeatureRelationService.updateBatchById(update); + } + + @PostMapping("/api/private/featureCode/resolve") + public Object resolveFeatureCode(@RequestBody ResolveFeatureCodeParam request) { + return featureCodeUtil.resolveFeatureCode(request.getFeatureCodes()); + } + + @PostMapping("/api/private/workspaceProduct/store") + public Object storeWorkspaceProduct(@RequestBody WorkspaceProductService.StoreWorkspaceProductParam request) { + workspaceProductService.storeWorkspaceProduct(request); + return "ok"; + } + + @PostMapping("/api/private/workspaceProduct/list") + public Object listWorkspaceProduct(@RequestBody WorkspaceProductService.WorkspaceProductParam request) { + return workspaceProductService.listWorkspaceProductCached(request); + } + + @PostMapping("/api/private/cacheWorkspaceProductHandler/process") + public Object processCacheWorkspaceProductHandler(@RequestBody ServicePkgProductCreatedPayload request) { + Event event = Event.builder() + .data(request) + .build(); + cacheWorkspaceProductHandler.onEvent(event, null); + return "ok"; + } + + @PostMapping("/api/private/role/insert") + public Object insertRole(@RequestPart("file") MultipartFile file) throws IOException { + + List importExcels = EasyExcel.read(file.getInputStream()) + .head(CreateRoleParam.class) + .sheet() + .doReadSync(); + + importExcels = importExcels.stream() + .filter(e -> Objects.equals(e.getType(), "c")) + .filter(e -> Objects.nonNull(e.getGroupId())) + .collect(Collectors.toList()); + + Set roleGroupIds = importExcels.stream() + .map(CreateRoleParam::getGroupId) + .collect(Collectors.toSet()); + + Map saasRoleGroups = saasRoleGroupDao.listByIds(roleGroupIds).stream() + .collect(Collectors.toMap(SaasRoleGroup::getId, Function.identity())); + + importExcels.forEach(e -> { + + SaasRoleGroup saasRoleGroup = saasRoleGroups.get(e.getGroupId()); + if (saasRoleGroup == null) { + log.info("not found saasRoleGroup,{}", e.getGroupId()); + return; + } + + SaveOrUpdateRoleVO.GroupInfoVO groupInfoVO = new SaveOrUpdateRoleVO.GroupInfoVO(); + groupInfoVO.setId(e.getGroupId()); + groupInfoVO.setWorkspaceTypeCode(saasRoleGroup.getWorkspaceTypeCode()); + SaveOrUpdateRoleVO saveOrUpdateRoleVO = SaveOrUpdateRoleVO.builder() + .name(e.getRoleName()) + .roleType("init") + .workspaceId(-1L) + .ownerOuId(-1L) + .operatorId(2007696L) + .operatorName("李龙") + .groupTree(Lists.newArrayList(groupInfoVO)) + .permissionGroupName("通用权限") + .permissionGroupType("feature") + .roleCode(e.getRoleCode()) + .isDisplay(true) + .enabled(true) + .build(); + try { + roleService.saveOrUpdate(saveOrUpdateRoleVO); + } catch (Exception ex) { + log.info("roleName {},ex:", e.getRoleName(), ex); + } + }); + return "ok"; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class CreateRoleParam { + + @ExcelProperty("角色名") + private String roleName; + + @ExcelProperty("分组ID") + private Long groupId; + + @ExcelProperty("单位类型type") + private Integer productUnitType; + + @ExcelProperty("角色编码") + private String roleCode; + + @ExcelProperty("操作类型") + private String type; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class ResolveFeatureCodeParam { + private Set featureCodes; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class RefreshFeatureResourcePathParam { + @NotBlank(message = "terminal不能为空") + private String terminal; + + @NotNull(message = "rootId不能为空") + private Long rootId; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class RefreshPgroupPermissionRelationParam { + + @NotNull(message = "workspaceType不能为空,(oms:6,政务:3)") + private Integer workspaceType; + @Builder.Default + private Integer pageSize = 10; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class RefreshPGroupPermissionFeatureParam { + + private List ids; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class RefreshProductPermissionFeatureParam { + + private List ids; + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/auth/TyrSaasAuthController.java b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/auth/TyrSaasAuthController.java index e94c775e..0bce0cb4 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/auth/TyrSaasAuthController.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/auth/TyrSaasAuthController.java @@ -63,7 +63,6 @@ public class TyrSaasAuthController implements TyrSaasAuthApi { } return ApiResult.ok(tyrSaasAuthService.findIdentityAuthMix(identityAuthReq)); } - @Override public ApiResult hasPermissionForIdentityV2(CheckIdentityPermissionReq req) { //单独做下参数校验 diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/FeatureResourceController.java b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/FeatureResourceController.java index 4818a562..fff49fd2 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/FeatureResourceController.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/FeatureResourceController.java @@ -2,10 +2,10 @@ package cn.axzo.tyr.server.controller.permission; import cn.axzo.framework.domain.web.result.ApiResult; import cn.axzo.tyr.client.feign.FeatureResourceApi; +import cn.axzo.tyr.client.model.req.DeleteFeatureResourceReq; import cn.axzo.tyr.client.model.req.FeatureResourceTreeSaveReq; import cn.axzo.tyr.client.model.req.GetFeatureResourceTreeReq; import cn.axzo.tyr.client.model.req.ResourceSyncReq; -import cn.axzo.tyr.client.model.res.FeatureResourceDetailResp; import cn.axzo.tyr.client.model.res.FeatureResourceTreeNode; import cn.axzo.tyr.server.service.FeatureResourceSyncService; import cn.axzo.tyr.server.service.SaasFeatureResourceService; @@ -15,6 +15,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.RestController; import java.util.List; +import java.util.Map; /** * 功能资源接口实现 @@ -39,6 +40,7 @@ public class FeatureResourceController implements FeatureResourceApi { @Override public ApiResult syncFromBase(ResourceSyncReq req) { + log.warn("sync menu, operatorId:{}", req.getOperatorId()); if (CollectionUtil.isEmpty(req.getIds())) { log.warn("no ids to sync from base env"); @@ -57,9 +59,9 @@ public class FeatureResourceController implements FeatureResourceApi { } @Override - public ApiResult deleteFeatureResource(Long featureId, Long operatorId) { - log.info("deleteFeatureResource featureId : {}, operatorId : {}", featureId, operatorId); - featureResourceService.deleteMenuFeature(featureId, operatorId); + public ApiResult deleteFeatureResource(DeleteFeatureResourceReq req) { + log.info("deleteFeatureResource req : {}", req); + featureResourceService.deleteFeatureResource(req); return ApiResult.ok(); } @@ -85,4 +87,9 @@ public class FeatureResourceController implements FeatureResourceApi { public ApiResult> getBaseTree(GetFeatureResourceTreeReq req) { return ApiResult.ok(featureResourceSyncService.getBaseTree(req)); } + + @Override + public ApiResult> listFeatureCodeByFeatureResourceIds(List featureResourceIds) { + return ApiResult.ok(featureResourceSyncService.listFeatureCodeByFeatureResourceIds(featureResourceIds)); + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/PageElementController.java b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/PageElementController.java new file mode 100644 index 00000000..7fc95122 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/PageElementController.java @@ -0,0 +1,54 @@ +package cn.axzo.tyr.server.controller.permission; + +import cn.axzo.framework.domain.web.result.ApiPageResult; +import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.tyr.client.feign.PageElementApi; +import cn.axzo.tyr.client.model.req.*; +import cn.axzo.tyr.client.model.res.GetUserHasPermissionPageElementResp; +import cn.axzo.tyr.client.model.res.PageElementResp; +import cn.axzo.tyr.server.service.SaasPageElementService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/6/18 + */ +@Slf4j +@RestController +@RequiredArgsConstructor +public class PageElementController implements PageElementApi { + + private final SaasPageElementService saasPageElementService; + + @Override + public ApiResult report(PageElementReportReq req) { + saasPageElementService.report(req); + return ApiResult.ok(); + } + + @Override + public ApiResult> getPageElement(GetPageElementReq req) { + return ApiResult.ok(saasPageElementService.getPageElement(req)); + } + + @Override + public ApiResult modifyPageElementRelation(ModifyPageElementRelationDTO req) { + saasPageElementService.modifyPageElementRelation(req); + return ApiResult.ok(); + } + + @Override + public ApiPageResult page(PageQueryElementReq req) { + return ApiPageResult.ok(saasPageElementService.page(req)); + } + + @Override + public ApiResult getUserHasPermissionPageElement(GetUserHasPermissionPageElementReq req) { + return ApiResult.ok(saasPageElementService.getUserHasPermissionPageElement(req)); + } +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/PermissionOperateLogController.java b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/PermissionOperateLogController.java new file mode 100644 index 00000000..7e0e3c6e --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/PermissionOperateLogController.java @@ -0,0 +1,28 @@ +package cn.axzo.tyr.server.controller.permission; + +import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.tyr.client.feign.PermissionOperateLogApi; +import cn.axzo.tyr.client.model.req.PermissionOperateLogReq; +import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationOperateLogService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/7/25 + */ +@Slf4j +@RestController +@RequiredArgsConstructor +public class PermissionOperateLogController implements PermissionOperateLogApi { + + private final SaasPgroupPermissionRelationOperateLogService saasPgroupPermissionRelationOperateLogService; + + @Override + public ApiResult save(PermissionOperateLogReq req) { + saasPgroupPermissionRelationOperateLogService.save(req); + return ApiResult.ok(); + } +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/PermissionPointController.java b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/PermissionPointController.java index f94b1bee..baf0f1fc 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/PermissionPointController.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/PermissionPointController.java @@ -2,6 +2,7 @@ package cn.axzo.tyr.server.controller.permission; import cn.axzo.framework.domain.web.result.ApiResult; import cn.axzo.tyr.client.feign.PermissionPointApi; +import cn.axzo.tyr.client.model.permission.DeletePermissionPointRequest; import cn.axzo.tyr.client.model.permission.PermissionPointDTO; import cn.axzo.tyr.client.model.permission.PermissionPointListQueryRequest; import cn.axzo.tyr.client.model.permission.PermissionPointMoveRequest; @@ -59,6 +60,9 @@ public class PermissionPointController implements PermissionPointApi { return ApiResult.ok(permissionPointService.delete(permissionId)); } + @Override public ApiResult> deletePermissionPointV2(DeletePermissionPointRequest request) { + return ApiResult.ok(permissionPointService.deleteV2(request)); + } @Override public ApiResult move(PermissionPointMoveRequest request) { diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/PermissionQueryController.java b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/PermissionQueryController.java index 2d7f22d9..70c389f1 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/PermissionQueryController.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/permission/PermissionQueryController.java @@ -6,7 +6,12 @@ import cn.axzo.tyr.client.model.req.NavTreeReq; import cn.axzo.tyr.client.model.req.PagePermissionReq; import cn.axzo.tyr.client.model.req.PagePermissionResp; import cn.axzo.tyr.client.model.req.PermissionCheckReq; +import cn.axzo.tyr.client.model.req.TreePermissionReq; +import cn.axzo.tyr.client.model.req.TreeProductFeatureResourceReq; +import cn.axzo.tyr.client.model.res.FeatureResourceDTO; import cn.axzo.tyr.client.model.res.NavTreeResp; +import cn.axzo.tyr.client.model.res.ProductFeatureResourceResp; +import cn.axzo.tyr.client.model.res.TreePermissionResp; import cn.axzo.tyr.server.service.PermissionQueryService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -42,4 +47,19 @@ public class PermissionQueryController implements PermissionQueryApi { public ApiResult hasPermission(PermissionCheckReq req) { return ApiResult.ok(permissionService.hasPermission(req)); } + + @Override + public ApiResult> treeProduct(TreeProductFeatureResourceReq request) { + return ApiResult.ok(permissionService.treeProduct(request)); + } + + @Override + public ApiResult> listFeatureResource(TreeProductFeatureResourceReq request) { + return ApiResult.ok(permissionService.listFeatureResource(request)); + } + + @Override + public ApiResult> treePermission(TreePermissionReq req) { + return ApiResult.ok(permissionService.treePermission(req)); + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/product/ProductController.java b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/product/ProductController.java index bc2d5381..297726fd 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/product/ProductController.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/product/ProductController.java @@ -3,7 +3,9 @@ package cn.axzo.tyr.server.controller.product; import cn.axzo.framework.domain.web.result.ApiPageResult; import cn.axzo.framework.domain.web.result.ApiResult; import cn.axzo.tyr.client.feign.ProductApi; +import cn.axzo.tyr.client.model.product.OldUpdateFeatureRelationRequestV2; import cn.axzo.tyr.client.model.product.ProductAddReq; +import cn.axzo.tyr.client.model.product.ProductDetailReq; import cn.axzo.tyr.client.model.product.ProductFeatureRelationSearchReq; import cn.axzo.tyr.client.model.product.ProductFeatureRelationUpdateReq; import cn.axzo.tyr.client.model.product.ProductFeatureRelationVO; @@ -11,6 +13,10 @@ import cn.axzo.tyr.client.model.product.ProductSearchListReq; import cn.axzo.tyr.client.model.product.ProductSearchPageReq; import cn.axzo.tyr.client.model.product.ProductUpdateReq; import cn.axzo.tyr.client.model.product.ProductVO; +import cn.axzo.tyr.client.model.req.ProductSaveReq; +import cn.axzo.tyr.client.model.req.UpdateProductStatusReq; +import cn.axzo.tyr.client.model.res.GovernmentTerminalResp; +import cn.axzo.tyr.client.model.res.WorkspaceProductResp; import cn.axzo.tyr.server.model.PermissionCacheKey; import cn.axzo.tyr.server.service.PermissionCacheService; import cn.axzo.tyr.server.service.ProductFeatureRelationService; @@ -68,7 +74,7 @@ public class ProductController implements ProductApi { */ @Override public ApiResult getById(Long id) { - return productService.getById(id); + return productService.getById(id, Boolean.FALSE); } /** @@ -130,9 +136,67 @@ public class ProductController implements ProductApi { return productFeatureRelationService.updateFeatureRelation(req); } + @Override + public ApiResult updateFeatureRelationV2(OldUpdateFeatureRelationRequestV2 req) { + if(CollectionUtils.isEmpty(req.getRelations())) { + return ApiResult.ok(false); + } + permissionCacheService.markTempDisable(PermissionCacheKey.builder().disableAll(true).build()); + return productFeatureRelationService.updateFeatureRelationV2(req); + } @Override public ApiResult>> queryProductFeatureRelationByWorkspace(Set workspaceIds) { return ApiResult.ok(productFeatureRelationService.getByWorkspace(workspaceIds)); } + + /** + * 产品上下架 + * + * @param req 产品及状态信息 + * @return 返回是否更新成功 + */ + @Override + public ApiResult updateStatus(UpdateProductStatusReq req) { + return productService.updateStatus(req); + } + + /** + * 保存、更新产品(v2版本) + * + * @param req 产品信息 + * @return 返回产品ID + */ + @Override + public ApiResult saveOrUpdate(ProductSaveReq req) { + permissionCacheService.markTempDisable(PermissionCacheKey.builder().disableAll(true).build()); + return productService.saveOrUpdate(req); + } + + /** + * 获取政务端列表 + * + * @param terminal 政务端 + * @return 返回端列表 + */ + @Override + public ApiResult> getGovernmentTerminal(String terminal) { + return productService.getGovernmentTerminal(terminal); + } + + /** + * 根据租户类型查询产品信息(新增租户、开通管理) + * + * @param workspaceType 租户类型 + * @return 租户产品信息 + */ + @Override + public ApiResult getWorkspaceProduct(String workspaceType) { + return productService.getWorkspaceProduct(workspaceType); + } + + @Override + public ApiResult getDetail(ProductDetailReq req) { + return productService.getById(req.getProductId(), req.getQueryFeatureScope()); + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/role/SaasRoleController.java b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/role/SaasRoleController.java index 0f1e8b37..c2f8cabb 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/role/SaasRoleController.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/role/SaasRoleController.java @@ -35,7 +35,6 @@ import cn.axzo.tyr.client.model.vo.SaasRoleVO; import cn.axzo.tyr.client.model.vo.SaveOrUpdateRoleVO; import cn.axzo.tyr.server.model.PermissionCacheKey; import cn.axzo.tyr.server.repository.dao.SaasRoleGroupRelationDao; -import cn.axzo.tyr.server.repository.dao.SaasRoleUserRelationDao; import cn.axzo.tyr.server.repository.entity.SaasRole; import cn.axzo.tyr.server.repository.entity.SaasRoleGroupRelation; import cn.axzo.tyr.server.service.PermissionCacheService; @@ -43,7 +42,6 @@ import cn.axzo.tyr.server.service.RoleService; import cn.axzo.tyr.server.service.SaasCommonDictService; import cn.axzo.tyr.server.service.SaasRoleGroupRelationService; import cn.axzo.tyr.server.service.SaasRoleGroupService; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import lombok.RequiredArgsConstructor; @@ -82,8 +80,6 @@ public class SaasRoleController implements TyrSaasRoleApi { @Autowired PermissionCacheService permissionCacheService; @Autowired - private SaasRoleUserRelationDao saasRoleUserRelationDao; - @Autowired private SaasCommonDictService saasCommonDictService; @Autowired private SaasRoleGroupService saasRoleGroupService; @@ -195,7 +191,6 @@ public class SaasRoleController implements TyrSaasRoleApi { @Override public ApiResult> queryInitRoleByWorkspaceId(String workspaceType) { return ApiResult.ok(roleService.queryInitRoleByWorkspaceId(workspaceType)); - } @Override @@ -219,7 +214,9 @@ public class SaasRoleController implements TyrSaasRoleApi { // 因为根节点在roleGroup里面没有,都是workspaceTypeCode,描述是放在字典表里 List commonDicts = listRootRole(req); - + if (CollectionUtils.isEmpty(commonDicts)) { + return ApiListResult.ok(); + } List roots = commonDicts.stream() .map(e -> RoleTreeRes.builder() @@ -294,8 +291,8 @@ public class SaasRoleController implements TyrSaasRoleApi { public ApiPageResult page(PageRoleReq request) { RoleService.PageSaasRoleParam param = RoleService.PageSaasRoleParam.builder().build(); BeanUtils.copyProperties(request, param); - Page page = roleService.page(param); - return ApiPageResult.ok(page.getRecords(), page.getTotal(), (int) page.getCurrent(), (int) page.getSize()); + cn.axzo.foundation.page.PageResp page = roleService.page(param); + return ApiPageResult.ok(page.getData(), page.getTotal(), (int) page.getCurrent(), (int) page.getSize()); } @Override @@ -411,6 +408,8 @@ public class SaasRoleController implements TyrSaasRoleApi { .name(roleGroup.getName()) .type(ROLE_GROUP_TYPE) .idStr(roleGroup.getId() + ":" + ROLE_GROUP_TYPE) + .workspaceId(roleGroup.getWorkspaceId()) + .ouId(roleGroup.getOuId()) .build(); } @@ -451,18 +450,32 @@ public class SaasRoleController implements TyrSaasRoleApi { } private List listRootRole(TreeRoleReq req) { - CommonDictQueryReq commonDictQueryReq = CommonDictQueryReq.builder() - .codes(StringUtils.isBlank(req.getWorkspaceTypeCode()) ? null : Lists.newArrayList(req.getWorkspaceTypeCode())) - .scope("role") - .build(); + + List workspaceTypeCodes = StringUtils.isNotBlank(req.getWorkspaceTypeCode()) ? Lists.newArrayList(req.getWorkspaceTypeCode()) + : Lists.newArrayList(); + if (StringUtils.isNotBlank(req.getTerminal())) { - List workspaceTypeCodes = TERMINAL_WORKSPACE_CODES.get(req.getTerminal()); - if (CollectionUtils.isEmpty(workspaceTypeCodes)) { + List terminalWorkspaceTypeCodes = TERMINAL_WORKSPACE_CODES.get(req.getTerminal()); + if (CollectionUtils.isEmpty(terminalWorkspaceTypeCodes)) { return Collections.emptyList(); } - commonDictQueryReq.setCodes(workspaceTypeCodes); + + if (StringUtils.isBlank(req.getWorkspaceTypeCode())) { + workspaceTypeCodes = terminalWorkspaceTypeCodes; + } else { + workspaceTypeCodes = terminalWorkspaceTypeCodes.stream() + .filter(e -> Objects.equals(e, req.getWorkspaceTypeCode())) + .collect(Collectors.toList()); + if (CollectionUtils.isEmpty(workspaceTypeCodes)) { + return Collections.emptyList(); + } + } } + CommonDictQueryReq commonDictQueryReq = CommonDictQueryReq.builder() + .codes(workspaceTypeCodes) + .scope("role") + .build(); return saasCommonDictService.query(commonDictQueryReq); } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/role/SaasRoleGroupController.java b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/role/SaasRoleGroupController.java index 12b35461..9118d20d 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/role/SaasRoleGroupController.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/role/SaasRoleGroupController.java @@ -1,11 +1,18 @@ package cn.axzo.tyr.server.controller.role; import cn.axzo.basics.common.exception.ServiceException; +import cn.axzo.foundation.exception.Axssert; +import cn.axzo.foundation.page.PageResp; import cn.axzo.framework.domain.web.result.ApiResult; import cn.axzo.tyr.client.feign.SaasRoleGroupApi; +import cn.axzo.tyr.client.model.req.ListSaasRoleGroupParam; +import cn.axzo.tyr.client.model.req.PageSaasRoleGroupParam; import cn.axzo.tyr.client.model.req.QuerySaasRoleGroupReq; +import cn.axzo.tyr.client.model.req.SaasRoleGroupDeleteRequest; import cn.axzo.tyr.client.model.req.UpdateRoleGroupOffsetReq; +import cn.axzo.tyr.client.model.res.SaasRoleGroupDTO; import cn.axzo.tyr.client.model.vo.SaasRoleGroupVO; +import cn.axzo.tyr.server.repository.entity.SaasRoleGroup; import cn.axzo.tyr.server.service.SaasRoleGroupService; import com.google.common.collect.Lists; import lombok.RequiredArgsConstructor; @@ -17,6 +24,8 @@ import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; +import static cn.axzo.tyr.server.config.exception.BizResultCode.ROLE_GROUP_NOT_FOUND; + @Slf4j @RestController @RequiredArgsConstructor @@ -48,6 +57,12 @@ public class SaasRoleGroupController implements SaasRoleGroupApi { return ApiResult.ok(roleGroups.get(0)); } + @Override + public ApiResult deleteV2(SaasRoleGroupDeleteRequest request) { + saasRoleGroupService.deleteV2(request); + return ApiResult.ok(); + } + @Override public ApiResult delete(List ids) { saasRoleGroupService.delete(ids); @@ -64,42 +79,59 @@ public class SaasRoleGroupController implements SaasRoleGroupApi { if (request.getOffset() != 1 && request.getOffset() != -1) { throw new ServiceException("暂时只支持移动一个位置"); } - SaasRoleGroupVO saasRoleGroup = getById(request.getId()).getData(); + SaasRoleGroup saasRoleGroup = saasRoleGroupService.getById(request.getId()); + Axssert.checkNonNull(saasRoleGroup, ROLE_GROUP_NOT_FOUND); - List roleGroupList = saasRoleGroupService.getRoleGroupList(QuerySaasRoleGroupReq.builder() - .parentId(saasRoleGroup.getParentId()) - .workspaceTypeCode(Lists.newArrayList(saasRoleGroup.getWorkspaceTypeCode())) - .build()) + List roleGroupList = saasRoleGroupService.lambdaQuery() + .eq(SaasRoleGroup::getParentId, saasRoleGroup.getParentId()) + .eq(SaasRoleGroup::getWorkspaceTypeCode, saasRoleGroup.getWorkspaceTypeCode()) + .eq(SaasRoleGroup::getWorkspaceId, saasRoleGroup.getWorkspaceId()) + .eq(SaasRoleGroup::getOuId, saasRoleGroup.getOuId()) + .list() .stream() - .sorted(Comparator.comparing(SaasRoleGroupVO::getSort)) + .sorted(Comparator.comparing(SaasRoleGroup::getSort)) .collect(Collectors.toList()); - SaasRoleGroupVO exchangeRoleGroup = findExchangeRoleGroup(request, roleGroupList); + SaasRoleGroup exchangeRoleGroup = findExchangeRoleGroup(request, roleGroupList); if (exchangeRoleGroup == null) { throw new ServiceException("未找到可以移动的位置"); } saasRoleGroupService.saveOrUpdate(SaasRoleGroupVO.builder() .id(saasRoleGroup.getId()) .sort(exchangeRoleGroup.getSort()) + .workspaceId(saasRoleGroup.getWorkspaceId()) + .ouId(saasRoleGroup.getOuId()) .build()); saasRoleGroupService.saveOrUpdate(SaasRoleGroupVO.builder() .id(exchangeRoleGroup.getId()) .sort(saasRoleGroup.getSort()) + .workspaceId(exchangeRoleGroup.getWorkspaceId()) + .ouId(exchangeRoleGroup.getOuId()) .build()); return ApiResult.ok(); } + @Override + public ApiResult> page(PageSaasRoleGroupParam request) { + return ApiResult.ok(saasRoleGroupService.page(request)); + } + + @Override + public ApiResult> list(ListSaasRoleGroupParam request) { + return ApiResult.ok(saasRoleGroupService.list(request)); + } + /** * 只支持移动一位 * @param request * @param roleGroupList * @return */ - private SaasRoleGroupVO findExchangeRoleGroup(UpdateRoleGroupOffsetReq request, - List roleGroupList) { + private SaasRoleGroup findExchangeRoleGroup(UpdateRoleGroupOffsetReq request, + List roleGroupList) { List ids = roleGroupList.stream() - .map(SaasRoleGroupVO::getId) + .map(SaasRoleGroup::getId) .collect(Collectors.toList()); int currentIndex = ids.indexOf(request.getId()); // 下移一位 diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/roleuser/RoleUserController.java b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/roleuser/RoleUserController.java index 90a4b163..fe4919da 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/roleuser/RoleUserController.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/roleuser/RoleUserController.java @@ -6,8 +6,11 @@ import cn.axzo.framework.domain.web.result.ApiResult; import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; import cn.axzo.tyr.client.feign.TyrSaasRoleUserApi; import cn.axzo.tyr.client.model.enums.IdentityType; +import cn.axzo.tyr.client.model.roleuser.RoleUserUpdateReq; import cn.axzo.tyr.client.model.roleuser.dto.GetUserAutoOwnRoleResp; +import cn.axzo.tyr.client.model.roleuser.dto.GetUserFeatureResourceIdsResp; import cn.axzo.tyr.client.model.roleuser.dto.SaasRoleUserDTO; +import cn.axzo.tyr.client.model.roleuser.dto.SaasRoleUserV2DTO; import cn.axzo.tyr.client.model.roleuser.dto.SuperAminInfoResp; import cn.axzo.tyr.client.model.roleuser.req.*; import cn.axzo.tyr.server.model.PermissionCacheKey; @@ -87,6 +90,11 @@ public class RoleUserController implements TyrSaasRoleUserApi { return ApiResult.ok(saasRoleUserRelationService.list(param)); } + @Override + public ApiResult> roleUserListV2(@RequestBody @Valid ListRoleUserRelationParam param) { + return ApiResult.ok(saasRoleUserRelationService.listV2(param)); + } + @Override public ApiResult createSuperAdminRole(CreateSuperAdminRoleParam param) { saasRoleUserService.createSuperAdminRole(param); @@ -162,4 +170,14 @@ public class RoleUserController implements TyrSaasRoleUserApi { public ApiResult getUserAutoOwnRole(GetUserAutoOwnRoleReq req) { return ApiResult.ok(saasRoleUserService.getUserAutoOwnRole(req)); } + + @Override + public ApiResult getUserFeatureResourceIds(GetUserFeatureResourceIdsReq req) { + return ApiResult.ok(saasRoleUserService.getUserFeatureResourceIds(req)); + } + + @Override + public ApiResult batchUpdateById(List roleUserUpdateReqs) { + return ApiResult.ok(saasRoleUserService.batchUpdateById(roleUserUpdateReqs)); + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/event/outer/CacheWorkspaceProductHandler.java b/tyr-server/src/main/java/cn/axzo/tyr/server/event/outer/CacheWorkspaceProductHandler.java new file mode 100644 index 00000000..1667f04b --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/event/outer/CacheWorkspaceProductHandler.java @@ -0,0 +1,58 @@ +package cn.axzo.tyr.server.event.outer; + +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.EventConsumer; +import cn.axzo.framework.rocketmq.EventHandler; +import cn.axzo.tyr.server.event.payload.ServicePkgProductCreatedPayload; +import cn.axzo.tyr.server.service.WorkspaceProductService; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 缓存项目的产品 + */ +@Slf4j +@Component +public class CacheWorkspaceProductHandler implements EventHandler, InitializingBean { + + @Autowired + private EventConsumer eventConsumer; + @Autowired + private WorkspaceProductService workspaceProductService; + + @Override + public void onEvent(Event event, EventConsumer.Context context) { + log.info("begin cached workspace product handler rocketmq event: {}", event); + ServicePkgProductCreatedPayload payload = event.normalizedData(ServicePkgProductCreatedPayload.class); + + if (Objects.isNull(payload) || CollectionUtils.isEmpty(payload.getSaasServicePkgProducts())) { + return; + } + + WorkspaceProductService.WorkspaceProductDTO workspaceProductDTO = WorkspaceProductService.WorkspaceProductDTO.builder() + .workspaceId(payload.getSaasServicePackage().getSpaceId()) + .productIds(payload.getSaasServicePkgProducts().stream() + .map(ServicePkgProductCreatedPayload.SaasServicePkgProductPo::getProductId) + .collect(Collectors.toSet())) + .build(); + + WorkspaceProductService.StoreWorkspaceProductParam storeWorkspaceProductParam = WorkspaceProductService.StoreWorkspaceProductParam.builder() + .workspaceProducts(Lists.newArrayList(workspaceProductDTO)) + .build(); + workspaceProductService.storeWorkspaceProduct(storeWorkspaceProductParam); + log.info("end cached workspace product handler rocketmq event: {}", event); + } + + + @Override + public void afterPropertiesSet() throws Exception { + eventConsumer.registerHandler(EventTypeEnum.SERVICE_PKG_PRODUCT_CREATED.getEventCode(), this); + } +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/event/outer/EventTypeEnum.java b/tyr-server/src/main/java/cn/axzo/tyr/server/event/outer/EventTypeEnum.java new file mode 100644 index 00000000..0eec0401 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/event/outer/EventTypeEnum.java @@ -0,0 +1,31 @@ +package cn.axzo.tyr.server.event.outer; + +import cn.axzo.framework.rocketmq.Event; +import lombok.Getter; + +/** + * @Classname EventTypeEnum + * @Date 2021/2/7 6:05 下午 + * @Created by lilong + */ +@Getter +public enum EventTypeEnum { + + SERVICE_PKG_PRODUCT_CREATED("service-pkg-product", "service-pkg-product-created", "创建服务包的产品"), + ; + + EventTypeEnum(String model, String name, String desc) { + this.eventCode = Event.EventCode.builder() + .module(model) + .name(name) + .build(); + this.model = model; + this.name = name; + this.desc = desc; + } + + private String model; + private String name; + private String desc; + private Event.EventCode eventCode; +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/event/payload/ServicePkgProductCreatedPayload.java b/tyr-server/src/main/java/cn/axzo/tyr/server/event/payload/ServicePkgProductCreatedPayload.java new file mode 100644 index 00000000..edc3f961 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/event/payload/ServicePkgProductCreatedPayload.java @@ -0,0 +1,99 @@ +package cn.axzo.tyr.server.event.payload; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ServicePkgProductCreatedPayload implements Serializable { + + private SaasServicePackagePo saasServicePackage; + + private List saasServicePkgProducts; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class SaasServicePackagePo { + + private Long id; + + /** + * 单位ID + */ + private Long ouId; + /** + * 租户id + */ + private Long saasTenantId; + + /** + * 服务包类型: 1.企业空间 ,2.项目空间 ,3.政企空间,6.OMS空间 + */ + private Integer type; + + /** + * 空间id + */ + private Long spaceId; + + /** + * license 名称 + */ + private String name; + + /** + * 有效期:开始 + */ + private LocalDateTime effectiveDate; + + /** + * 有效斯:结束 + */ + private LocalDateTime ineffectiveDate; + + /** + * license状态 : 1.未生效 2.有效 3.限制使用 4.停用 5.过期 + */ + private Integer status; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class SaasServicePkgProductPo { + + private Long id; + + /** + * license id + */ + private Long servicePkgId; + + /** + * license相关联的产品 + */ + private Long productId; + + /** + * 产品功能名称 + */ + private String productName; + + /** + * 0是基础产品 1非基础产品 + */ + + private Boolean isBasic; + } +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/job/CMSOtherRoleJobHandler.java b/tyr-server/src/main/java/cn/axzo/tyr/server/job/CMSOtherRoleJobHandler.java index 7f61d3c8..a11560fb 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/job/CMSOtherRoleJobHandler.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/job/CMSOtherRoleJobHandler.java @@ -74,10 +74,10 @@ public class CMSOtherRoleJobHandler extends IJobHandler { log.info("CMSOtherRoleJobHandler start"); // 查询无法回溯的角色 List oldRole = roleDao.lambdaQuery() - .ne(SaasRole::getWorkspaceId, -1l) + .ne(SaasRole::getWorkspaceId, -1L) .eq(SaasRole::getRoleType, "init") .in(SaasRole::getFitOuTypeBit, Arrays.asList(1, 2, 4, 8, 16)) - .eq(SaasRole::getFromPreRoleId, 0l) + .eq(SaasRole::getFromPreRoleId, 0L) .eq(BaseEntity::getIsDelete, 0) .list(); if (CollectionUtils.isEmpty(oldRole)) { diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/job/CMSRoleJobHandler.java b/tyr-server/src/main/java/cn/axzo/tyr/server/job/CMSRoleJobHandler.java index f1d77672..db2c1c54 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/job/CMSRoleJobHandler.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/job/CMSRoleJobHandler.java @@ -141,8 +141,8 @@ public class CMSRoleJobHandler extends IJobHandler { roleGroup.setWorkspaceTypeCode(workspaceTypCode); roleGroup.setOuTypeCode(ouType); roleGroup.setName(groupName); - roleGroup.setWorkspaceId(-1l); - roleGroup.setOuId(-1l); + roleGroup.setWorkspaceId(-1L); + roleGroup.setOuId(-1L); roleGroup.setSort(1); roleGroupDao.save(roleGroup); @@ -151,10 +151,10 @@ public class CMSRoleJobHandler extends IJobHandler { SaasRole newRole = new SaasRole(); newRole.setRoleType(RoleTypeEnum.INIT.getValue()); newRole.setName(preRole.getName()); - newRole.setCreateBy(-1l); - newRole.setUpdateBy(-1l); - newRole.setOwnerOuId(-1l); - newRole.setWorkspaceId(-1l); + newRole.setCreateBy(-1L); + newRole.setUpdateBy(-1L); + newRole.setOwnerOuId(-1L); + newRole.setWorkspaceId(-1L); roleDao.save(newRole); // 创建角色分组关联关系 diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/job/OMSRoleJobHandler.java b/tyr-server/src/main/java/cn/axzo/tyr/server/job/OMSRoleJobHandler.java index b7167aea..71c6e11c 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/job/OMSRoleJobHandler.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/job/OMSRoleJobHandler.java @@ -64,8 +64,8 @@ public class OMSRoleJobHandler extends IJobHandler { roleGroup.setWorkspaceTypeCode("6"); roleGroup.setOuTypeCode("6"); roleGroup.setName("管理员"); - roleGroup.setWorkspaceId(-1l); - roleGroup.setOuId(-1l); + roleGroup.setWorkspaceId(-1L); + roleGroup.setOuId(-1L); roleGroup.setSort(1); roleGroupDao.save(roleGroup); // 查询OMS的角色 workspaceType=6 OMS的角色 @@ -75,8 +75,8 @@ public class OMSRoleJobHandler extends IJobHandler { .list(); // 重置老角色多余的字段 oldRole.forEach(e -> { - e.setWorkspaceId(-1l); - e.setOwnerOuId(-1l); + e.setWorkspaceId(-1L); + e.setOwnerOuId(-1L); }); roleDao.updateBatchById(oldRole); // 保存角色分组关联关系 diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/model/BasicRoleDO.java b/tyr-server/src/main/java/cn/axzo/tyr/server/model/BasicRoleDO.java new file mode 100644 index 00000000..2bbae81b --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/model/BasicRoleDO.java @@ -0,0 +1,33 @@ +package cn.axzo.tyr.server.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/7/25 + */ +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BasicRoleDO { + + /** + * 角色ID + */ + private Long roleId; + + /** + * 角色code + */ + private String roleCode; + + /** + * 角色名字 + */ + private String roleName; +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/model/PermissionCacheKey.java b/tyr-server/src/main/java/cn/axzo/tyr/server/model/PermissionCacheKey.java index c039e546..5d05d415 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/model/PermissionCacheKey.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/model/PermissionCacheKey.java @@ -33,6 +33,8 @@ public class PermissionCacheKey { private Boolean disableAll; public String buildAuthKey() { + // 因为personId、identityId、identityType这三个参数用户现在可以随意组合 + // return personId == null ? KeyUtil.buildKeyBySeparator("auth-i", identityId, identityType.getCode(), ouId, workspaceId) : KeyUtil.buildKeyBySeparator("auth-p", personId, ouId, workspaceId); } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/model/RelationOperateLogProductBindResourceDO.java b/tyr-server/src/main/java/cn/axzo/tyr/server/model/RelationOperateLogProductBindResourceDO.java new file mode 100644 index 00000000..a2fcf027 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/model/RelationOperateLogProductBindResourceDO.java @@ -0,0 +1,30 @@ +package cn.axzo.tyr.server.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/7/24 + */ +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class RelationOperateLogProductBindResourceDO { + + /** + * 资源编码 + */ + private List uniCodes; + + /** + * 产品ID + */ + private Long productId; +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/model/RelationOperateLogResourceBindElementDO.java b/tyr-server/src/main/java/cn/axzo/tyr/server/model/RelationOperateLogResourceBindElementDO.java new file mode 100644 index 00000000..2921d591 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/model/RelationOperateLogResourceBindElementDO.java @@ -0,0 +1,30 @@ +package cn.axzo.tyr.server.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/7/24 + */ +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class RelationOperateLogResourceBindElementDO { + + /** + * 资源编码 + */ + private String uniCode; + + /** + * 页面元素code列表 + */ + private List featureCodes; +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/model/RelationOperateLogResourceBindRoleDO.java b/tyr-server/src/main/java/cn/axzo/tyr/server/model/RelationOperateLogResourceBindRoleDO.java new file mode 100644 index 00000000..59376644 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/model/RelationOperateLogResourceBindRoleDO.java @@ -0,0 +1,30 @@ +package cn.axzo.tyr.server.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/7/24 + */ +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class RelationOperateLogResourceBindRoleDO { + + /** + * 资源编码 + */ + private String uniCode; + + /** + * 角色code列表 + */ + private List roleCodes; +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/model/RelationOperateLogRoleBindResourceDO.java b/tyr-server/src/main/java/cn/axzo/tyr/server/model/RelationOperateLogRoleBindResourceDO.java new file mode 100644 index 00000000..db7fddb5 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/model/RelationOperateLogRoleBindResourceDO.java @@ -0,0 +1,30 @@ +package cn.axzo.tyr.server.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/7/24 + */ +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class RelationOperateLogRoleBindResourceDO { + + /** + * 资源编码 + */ + private List uniCodes; + + /** + * 角色code + */ + private String roleCode; +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/model/ResourcePermission.java b/tyr-server/src/main/java/cn/axzo/tyr/server/model/ResourcePermission.java index 02460801..046f712a 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/model/ResourcePermission.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/model/ResourcePermission.java @@ -27,4 +27,9 @@ public class ResourcePermission { private Integer featureType; private Integer authType; + + /** + * 资源所属端 + */ + private String terminal; } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/SaasBasicDictDao.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/SaasBasicDictDao.java index 82516fe2..36e98ade 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/SaasBasicDictDao.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/SaasBasicDictDao.java @@ -12,6 +12,7 @@ import cn.axzo.tyr.server.repository.mapper.SaasBasicDictMapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Service; import java.util.List; @@ -33,7 +34,8 @@ public class SaasBasicDictDao extends ServiceImpl getByResourceTreeParam(GetFeatureResourceTreeReq req) { return this.lambdaQuery().select(SaasFeatureResource::getId, SaasFeatureResource::getFeatureCode, SaasFeatureResource::getFeatureName, SaasFeatureResource::getFeatureType, SaasFeatureResource::getTerminal, SaasFeatureResource::getParentId, - SaasFeatureResource::getDisplayOrder) + SaasFeatureResource::getDisplayOrder, + SaasFeatureResource::getWorkspaceType) .eq(SaasFeatureResource::getIsDelete, TableIsDeleteEnum.NORMAL.value) .in(CollectionUtils.isNotEmpty(req.getTerminals()), SaasFeatureResource::getTerminal, req.getTerminals()) .eq(Objects.nonNull(req.getStatus()), SaasFeatureResource::getStatus, req.getStatus()) diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/dao/SaasPageElementDao.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/dao/SaasPageElementDao.java new file mode 100644 index 00000000..cefcefed --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/dao/SaasPageElementDao.java @@ -0,0 +1,44 @@ +package cn.axzo.tyr.server.repository.dao; + +import cn.axzo.basics.common.constant.enums.DeleteEnum; +import cn.axzo.tyr.server.repository.entity.SaasPageElement; +import cn.axzo.tyr.server.repository.entity.SaasPageElementFeatureResourceRelation; +import cn.axzo.tyr.server.repository.mapper.SaasPageElementMapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Objects; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/6/18 + */ +@Repository +public class SaasPageElementDao extends ServiceImpl { + + public void deleteAllByTerminal(String terminal) { + lambdaUpdate() + .eq(SaasPageElement::getTerminal, terminal) + .set(SaasPageElement::getIsDelete, DeleteEnum.DELETE.getValue()) + .update(); + } + + public List listByCodes(List codes, String terminal) { + return lambdaQuery() + .eq(SaasPageElement::getTerminal, terminal) + .in(SaasPageElement::getCode, codes) + .list(); + } + + public List listByGroupCodesAndExcludeIds(List groupCodes, List excludeIds, String terminal, List types) { + return lambdaQuery() + .eq(SaasPageElement::getTerminal, terminal) + .in(SaasPageElement::getGroupCode, groupCodes) + .in(CollectionUtils.isNotEmpty(types), SaasPageElement::getType, types) + .notIn(CollectionUtils.isNotEmpty(excludeIds), SaasPageElement::getId, excludeIds) + .list(); + } +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/dao/SaasPageElementFeatureResourceRelationDao.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/dao/SaasPageElementFeatureResourceRelationDao.java new file mode 100644 index 00000000..c52bc145 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/dao/SaasPageElementFeatureResourceRelationDao.java @@ -0,0 +1,49 @@ +package cn.axzo.tyr.server.repository.dao; + +import cn.axzo.basics.common.constant.enums.DeleteEnum; +import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import cn.axzo.tyr.server.repository.entity.SaasPageElementFeatureResourceRelation; +import cn.axzo.tyr.server.repository.mapper.SaasPageElementFeatureResourceRelationMapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Objects; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/6/18 + */ +@Repository +public class SaasPageElementFeatureResourceRelationDao extends ServiceImpl { + + public List listByPageElementCode(String pageElementCode, String terminal, List types) { + return lambdaQuery() + .eq(SaasPageElementFeatureResourceRelation::getIsDelete, DeleteEnum.NORMAL.getValue()) + .eq(SaasPageElementFeatureResourceRelation::getPageElementCode, pageElementCode) + .eq(SaasPageElementFeatureResourceRelation::getTerminal, terminal) + .in(CollectionUtils.isNotEmpty(types), SaasPageElementFeatureResourceRelation::getType, types) + .orderByDesc(BaseEntity::getId) + .list(); + } + public List listByUniCodeAndTerminal(List featureResourceUniCodes, String terminal, List types) { + return lambdaQuery() + .eq(SaasPageElementFeatureResourceRelation::getIsDelete, DeleteEnum.NORMAL.getValue()) + .in(SaasPageElementFeatureResourceRelation::getFeatureResourceUniCode, featureResourceUniCodes) + .in(CollectionUtils.isNotEmpty(types), SaasPageElementFeatureResourceRelation::getType, types) + .eq(SaasPageElementFeatureResourceRelation::getTerminal, terminal) + .list(); + } + + public void deleteByTerminalAndUniCodes(String terminal, List featureResourceUniCodes, List types, Long operatorId) { + lambdaUpdate() + .eq(SaasPageElementFeatureResourceRelation::getTerminal, terminal) + .in(SaasPageElementFeatureResourceRelation::getFeatureResourceUniCode, featureResourceUniCodes) + .in(CollectionUtils.isNotEmpty(types), SaasPageElementFeatureResourceRelation::getType, types) + .set(SaasPageElementFeatureResourceRelation::getIsDelete, DeleteEnum.DELETE.getValue()) + .set(Objects.nonNull(operatorId), SaasPageElementFeatureResourceRelation::getUpdateBy, operatorId) + .update(); + } +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/dao/SaasPgroupPermissionRelationOperateLogDao.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/dao/SaasPgroupPermissionRelationOperateLogDao.java new file mode 100644 index 00000000..798ebac6 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/dao/SaasPgroupPermissionRelationOperateLogDao.java @@ -0,0 +1,12 @@ +package cn.axzo.tyr.server.repository.dao; + +import cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelationOperateLog; +import cn.axzo.tyr.server.repository.mapper.SaasPgroupPermissionRelationOperateLogMapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Repository; + +@Repository +public class SaasPgroupPermissionRelationOperateLogDao extends ServiceImpl { + +} + diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/dao/SaasRoleDao.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/dao/SaasRoleDao.java index 4f11e3fc..888a33d3 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/dao/SaasRoleDao.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/dao/SaasRoleDao.java @@ -17,6 +17,7 @@ import org.springframework.stereotype.Repository; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -120,5 +121,15 @@ public class SaasRoleDao extends ServiceImpl { public List listFeatureByIds(Set roleIds, Set featureIds) { return this.baseMapper.listFeatureByIds(roleIds, featureIds); } + + public List listByRoleCodes(List roleCodes) { + if (CollectionUtils.isEmpty(roleCodes)) { + return Collections.emptyList(); + } + return lambdaQuery().in(SaasRole::getRoleCode, roleCodes) + .eq(BaseEntity::getIsDelete, TableIsDeleteEnum.NORMAL) + .eq(SaasRole::getRoleType, RoleTypeEnum.INIT.getValue()) + .list(); + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/dao/SaasRoleGroupDao.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/dao/SaasRoleGroupDao.java index cfc9921a..470a9081 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/dao/SaasRoleGroupDao.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/dao/SaasRoleGroupDao.java @@ -39,6 +39,7 @@ public class SaasRoleGroupDao extends ServiceImpl id) { lambdaUpdate() .in(BaseEntity::getId,id) - .set(BaseEntity::getIsDelete,1l) + .set(BaseEntity::getIsDelete, 1L) .update(); } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/DataObjectRule.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/DataObjectRule.java index 072819e7..7cf0f261 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/DataObjectRule.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/DataObjectRule.java @@ -34,7 +34,7 @@ public class DataObjectRule extends BaseOperatorEntity implement private String name; /** * 行级数据权限(单选) 1:仅本人数据 2:本人及下属数据 3:仅本部门数据 4:本部门及以下数据 - * 5:仅本单位数据 6:本单位及下级直属单位数据 7:本单位及下级协同(直属+合作)单位数据 8:本单位及以下协同(直属+合作)单位数据 9:本项目部数据 + * 5:仅本单位数据 6:本单位及下级直属单位数据 7:本单位及下级协同(直属+合作)单位数据 8:本单位及以下协同(直属+合作)单位数据 9:本项目数据 */ private Integer rowPermission; /** diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/DataObjectRuleAttr.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/DataObjectRuleAttr.java index f4df9878..3c97cf72 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/DataObjectRuleAttr.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/DataObjectRuleAttr.java @@ -41,7 +41,7 @@ public class DataObjectRuleAttr extends BaseOperatorEntity i private Integer sort; /** * 字段值查看范围 1:仅本人数据 2:本人及下属数据 3:仅本部门数据 4:本部门及以下数据 - * 5:仅本单位数据 6:本单位及下级直属单位数据 7:本单位及下级协同(直属+合作)单位数据 8:本单位及以下协同(直属+合作)单位数据 9:本项目部数据 + * 5:仅本单位数据 6:本单位及下级直属单位数据 7:本单位及下级协同(直属+合作)单位数据 8:本单位及以下协同(直属+合作)单位数据 9:本项目数据 * 10:同行级数据权限 */ private Integer visibilityScope; diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/ProductFeatureQuery.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/ProductFeatureQuery.java index b72f1969..9a7a6a1d 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/ProductFeatureQuery.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/ProductFeatureQuery.java @@ -1,5 +1,7 @@ package cn.axzo.tyr.server.repository.entity; +import cn.axzo.tyr.client.common.enums.FeatureResourceType; +import cn.axzo.tyr.client.model.req.FeatureIdPair; import lombok.Builder; import lombok.Data; @@ -22,6 +24,18 @@ public class ProductFeatureQuery { private String terminal; private Integer workspaceJoinType; - +// private Set featureIds; + + /** + * 菜单资源数节点类型 + */ + private List featureResourceTypes; + + private List featureIdPairs; + + /** + * 区分新老菜单资源树 + */ + private Integer type; } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/ProductModule.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/ProductModule.java index 8d9bb5af..f7b523b5 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/ProductModule.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/ProductModule.java @@ -1,11 +1,11 @@ package cn.axzo.tyr.server.repository.entity; import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import cn.axzo.tyr.server.repository.handler.ProductModuleSkuHandler; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; +import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; +import lombok.*; import org.apache.commons.lang3.StringUtils; import java.io.Serializable; @@ -13,7 +13,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; -import java.util.stream.Stream; /** * saas-产品表(SaasProduct)表实体类 @@ -25,7 +24,7 @@ import java.util.stream.Stream; @Setter @ToString @EqualsAndHashCode(callSuper = true) -@TableName("product_module") +@TableName(value = "product_module", autoResultMap = true) public class ProductModule extends BaseEntity { /** @@ -82,6 +81,56 @@ public class ProductModule extends BaseEntity { */ private String ouType; + /** + * 创建人 + */ + private Long createBy; + + /** + * 修改人 + */ + private Long updateBy; + + /** + * 产品类型 + * PRODUCT_VERSION:产品版本类型、ADD_VALUE_SERVICE:增值服务类型、 + * GENERAL_SERVICE:通用产品类型、HARD_WARE:硬件产品类型 + */ + private String category; + + /** + * 版本升级序列(数字越小,版本越低,不能降级,只能升级) <企业、项目产品> + */ + private Integer version; + + /** + * 人数上限 <企业、项目产品> + */ + private Integer maxPersonCount; + + /** + * 最大项目数 <企业产品> + */ + private Integer maxWorkspaceCount; + + /** + * 价格(单位:分) + */ + private Long price; + + /** + * 产品详情 jsonList(skuNameSKU名称、model规格型号、count数量、unit单位) + */ + @TableField(value = "skus", typeHandler = ProductModuleSkuHandler.class) + private List skus; + + /** + * 素材<仅硬件产品支持>json类型 + * ({"miages":List,"videos":List,"detailImages":List}) + */ + @TableField(value = "material", typeHandler = FastjsonTypeHandler.class) + private Material material; + /** * 获取主键值 * @@ -100,5 +149,54 @@ public class ProductModule extends BaseEntity { return new ArrayList<>(); } } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Sku { + + /** + * SKU名称 + */ + private String skuName; + + /** + * 规格型号 + */ + private String model; + + /** + * 数量 + */ + private Integer count; + + /** + * 单位 + */ + private String unit; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class Material { + + /** + * 图片 + */ + private List images; + + /** + * 视频 + */ + private List videos; + + /** + * 详情大图 + */ + private List detailImages; + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/SaasFeatureResource.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/SaasFeatureResource.java index 0a08b04a..526e5dc7 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/SaasFeatureResource.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/SaasFeatureResource.java @@ -1,14 +1,20 @@ package cn.axzo.tyr.server.repository.entity; import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import cn.axzo.tyr.client.model.base.FeatureResourceExtraDO; import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.Getter; import lombok.NoArgsConstructor; +import org.apache.commons.lang3.StringUtils; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; /** @@ -22,15 +28,32 @@ import java.util.stream.Collectors; @Data @NoArgsConstructor @AllArgsConstructor -@TableName("saas_feature_resource") -public class SaasFeatureResource extends BaseEntity { +@TableName(value = "saas_feature_resource", autoResultMap = true) +public class SaasFeatureResource extends BaseEntity { private static final long serialVersionUID = 1L; + /** + * 数据库默认的workspaceType + */ + public static final Long DEFAULT_WORKSPACE_TYPE = 0L; + + /** + * 显示状态 + */ + public static final Integer DISPLAY_STATUS = 1; + + /** + * 隐藏状态 + */ + public static final Integer HIDE_STATUS = 0; + /** * 资源编码-权限码 + * 废弃,同步数据使用uniCode */ + @Deprecated private String featureCode; /** @@ -39,7 +62,7 @@ public class SaasFeatureResource extends BaseEntity { private String featureName; /** - * 资源类型1-菜单 2-页面 3-应用入口 4-组件 + * 资源类型1-菜单 2-页面 3-应用入口 4-组件;5-root节点 */ private Integer featureType; @@ -111,7 +134,8 @@ public class SaasFeatureResource extends BaseEntity { /** * 扩展字段 */ - private String extra; + @TableField(value = "extra", typeHandler = FastjsonTypeHandler.class) + private FeatureResourceExtraDO extra; /** * 授权类型0-全部角色 1-指定角色 @@ -133,12 +157,40 @@ public class SaasFeatureResource extends BaseEntity { */ private Long updateBy; + /** + * 应用范围(租户类型):1:企业工作台 2;项目工作台 + */ + private Long workspaceType; + + /** + * 最低版本序列,主要支持版本灰度策略 + */ + private Integer version; + + /** + * 唯一编码,用于pre环境菜单同步 + */ + private String uniCode; + public List splitPath() { if (StrUtil.isBlank(this.path)) { return Collections.emptyList(); } - return StrUtil.split(this.path, ",").stream().map(Long::valueOf).collect(Collectors.toList()); + return StrUtil.split(this.path, ",").stream().filter(StringUtils::isNotBlank).map(Long::valueOf).collect(Collectors.toList()); } + @Getter + @AllArgsConstructor + public enum AuthType { + ALL_ROLE(0, "全部角色"), + ASSIGN_ROLE(1, "指定角色"); + private Integer value; + + private String desc; + + public static boolean isAllRole(Integer authType) { + return Objects.equals(ALL_ROLE.getValue(), authType); + } + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/SaasPageElement.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/SaasPageElement.java new file mode 100644 index 00000000..f5327ea1 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/SaasPageElement.java @@ -0,0 +1,64 @@ +package cn.axzo.tyr.server.repository.entity; + +import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 页面元素表 + * + * @author likunpeng + * @version 1.0 + * @date 2024/6/18 + */ +@Getter +@Setter +@ToString +@Builder +@EqualsAndHashCode(callSuper = true) +@TableName("saas_page_element") +public class SaasPageElement extends BaseEntity { + + /** + * 元素的组编码 + */ + @TableField("group_code") + private String groupCode; + + /** + * 页面、元素编码 + */ + @TableField("code") + private String code; + + /** + * 页面、元素名称 + */ + @TableField("name") + private String name; + + /** + * 页面、元素类型(PAGE:页面,COMPONENT:组件) + */ + @TableField("type") + private String type; + + /** + * 页面路由地址 + */ + @TableField("link_url") + private String linkUrl; + + /** + * 所属端 + */ + @TableField("terminal") + private String terminal; + + /** + * 创建人名 + */ + @TableField("create_name") + private String createName; +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/SaasPageElementFeatureResourceRelation.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/SaasPageElementFeatureResourceRelation.java new file mode 100644 index 00000000..08a6df58 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/SaasPageElementFeatureResourceRelation.java @@ -0,0 +1,59 @@ +package cn.axzo.tyr.server.repository.entity; + +import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 页面元素与菜单组件关系表 + * + * @author likunpeng + * @version 1.0 + * @date 2024/6/18 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@TableName(value = "saas_page_element_feature_resource_relation", autoResultMap = true) +public class SaasPageElementFeatureResourceRelation extends BaseEntity { + + + /** + * 绑定类型:0(默认类型) 1(页面默认路由) + * @see cn.axzo.tyr.client.common.enums.PageElementFeatureResourceRelationTypeEnum + */ + @TableField("type") + private Integer type; + + /** + * 创建人 + */ + @TableField("create_by") + private Long createBy; + + /** + * 更新人 + */ + @TableField("update_by") + private Long updateBy; + + /** + * 页面元素code + */ + @TableField("page_element_code") + private String pageElementCode; + + /** + * 菜单组件code + */ + @TableField("feature_resource_uni_code") + private String featureResourceUniCode; + + /** + * 所属端 + */ + @TableField("terminal") + private String terminal; +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/SaasPgroupPermissionRelation.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/SaasPgroupPermissionRelation.java index 982d7452..3ab81d9d 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/SaasPgroupPermissionRelation.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/SaasPgroupPermissionRelation.java @@ -1,7 +1,10 @@ package cn.axzo.tyr.server.repository.entity; import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import com.alibaba.fastjson.JSONArray; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; @@ -23,6 +26,9 @@ import java.util.Objects; @TableName("saas_pgroup_permission_relation") public class SaasPgroupPermissionRelation extends BaseEntity { + public static final Integer OLD_FEATURE = 0; + + public static final Integer NEW_FEATURE = 1; /** * 权限集id @@ -43,6 +49,26 @@ public class SaasPgroupPermissionRelation extends BaseEntity { + + /** + * 操作关联的表名 + */ + private String tableName; + + /** + * 操作场景 + * @see PermissionRelationOperateLogSceneEnum + */ + private String scene; + + /** + * 操作场景ID + */ + private String sceneId; + + /** + * 请求ID + */ + private String traceId; + + /** + * 请求参数数据 + */ + private String requestData; + + /** + * 操作后数据 + */ + private String operateData; + + /** + * 创建者 + */ + private Long createBy; + + /** + * 操作人名 + */ + private String createByName; + + /** + * 操作人角色 + */ + private String createByRole; +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/SaasProductModuleFeatureRelation.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/SaasProductModuleFeatureRelation.java index ea2d0a19..7d9c5edf 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/SaasProductModuleFeatureRelation.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/SaasProductModuleFeatureRelation.java @@ -42,6 +42,21 @@ public class SaasProductModuleFeatureRelation extends BaseEntity extends BaseTypeHandler> { + + @Override + public void setNonNullParameter(PreparedStatement ps, int i, List parameter, JdbcType jdbcType) throws SQLException { + ps.setString(i, JSON.toJSONString(parameter)); + } + + @Override + public List getNullableResult(ResultSet rs, String columnName) throws SQLException { + return rs.wasNull() ? null : JSON.parseObject(rs.getString(columnName), specificType()); + } + + @Override + public List getNullableResult(ResultSet rs, int columnIndex) throws SQLException { + return rs.wasNull() ? null : JSON.parseObject(rs.getString(columnIndex), specificType()); + } + + @Override + public List getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { + return cs.wasNull() ? null : JSON.parseObject(cs.getString(columnIndex), specificType()); + } + + protected abstract TypeReference> specificType(); +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/handler/ProductModuleSkuHandler.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/handler/ProductModuleSkuHandler.java new file mode 100644 index 00000000..9ed3afc0 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/handler/ProductModuleSkuHandler.java @@ -0,0 +1,20 @@ +package cn.axzo.tyr.server.repository.handler; + +import cn.axzo.tyr.server.repository.entity.ProductModule; +import com.alibaba.fastjson.TypeReference; +import org.apache.ibatis.type.MappedTypes; + +import java.util.List; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/5/6 + */ +@MappedTypes({List.class}) +public class ProductModuleSkuHandler extends ListTypeHandler { + @Override + protected TypeReference> specificType() { + return new TypeReference>(){}; + } +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasFeatureMapper.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasFeatureMapper.java index c72c89c1..b93def44 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasFeatureMapper.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasFeatureMapper.java @@ -2,6 +2,8 @@ package cn.axzo.tyr.server.repository.mapper; import cn.axzo.tyr.server.repository.entity.SaasFeature; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; /** @@ -19,4 +21,6 @@ public interface SaasFeatureMapper extends BaseMapper { "WHERE path LIKE CONCAT(#{pathPrefix},'%') ") void updateChildrenPath(Long updater, String pathPrefix, String newPathPrefix); + @Select("SELECT * FROM saas_feature WHERE id = #{id}") + SaasFeature getByIdDeleteAware(@Param("id") Long id); } \ No newline at end of file diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasPageElementFeatureResourceRelationMapper.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasPageElementFeatureResourceRelationMapper.java new file mode 100644 index 00000000..3d0597f8 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasPageElementFeatureResourceRelationMapper.java @@ -0,0 +1,9 @@ +package cn.axzo.tyr.server.repository.mapper; + +import cn.axzo.tyr.server.repository.entity.SaasPageElementFeatureResourceRelation; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +public interface SaasPageElementFeatureResourceRelationMapper extends BaseMapper { + +} + diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasPageElementMapper.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasPageElementMapper.java new file mode 100644 index 00000000..1ca5d1b9 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasPageElementMapper.java @@ -0,0 +1,9 @@ +package cn.axzo.tyr.server.repository.mapper; + +import cn.axzo.tyr.server.repository.entity.SaasPageElement; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +public interface SaasPageElementMapper extends BaseMapper { + +} + diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasPgroupPermissionRelationOperateLogMapper.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasPgroupPermissionRelationOperateLogMapper.java new file mode 100644 index 00000000..33e43629 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasPgroupPermissionRelationOperateLogMapper.java @@ -0,0 +1,11 @@ +package cn.axzo.tyr.server.repository.mapper; + +import cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelationOperateLog; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface SaasPgroupPermissionRelationOperateLogMapper extends BaseMapper { + +} + diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasRoleGroupMapper.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasRoleGroupMapper.java index e6c7c56d..f083db31 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasRoleGroupMapper.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasRoleGroupMapper.java @@ -3,9 +3,21 @@ package cn.axzo.tyr.server.repository.mapper; import cn.axzo.tyr.server.repository.entity.SaasRoleGroup; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; @Mapper public interface SaasRoleGroupMapper extends BaseMapper { + @Select("") + List getByIdsDeleteAware( + @Param("ids") List ids); + } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasRoleMapper.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasRoleMapper.java index 23ea0923..3a992c58 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasRoleMapper.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/SaasRoleMapper.java @@ -26,8 +26,6 @@ public interface SaasRoleMapper extends BaseMapper { List listForOUWorkspace(Long ouId, Long workspaceId, Integer workspaceJoinType); - List listRoleByFeatures(@Param("featureIds") Set featureIds); - List listFeatureByIds(@Param("roleIds") Set roleIds, @Param("featureIds") Set featureIds); } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/FeatureResourceSyncService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/FeatureResourceSyncService.java index 875cb911..f3ccccb2 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/FeatureResourceSyncService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/FeatureResourceSyncService.java @@ -5,6 +5,7 @@ import cn.axzo.tyr.client.model.req.ResourceSyncReq; import cn.axzo.tyr.client.model.res.FeatureResourceTreeNode; import java.util.List; +import java.util.Map; /** * 功能资源同步服务 @@ -20,4 +21,6 @@ public interface FeatureResourceSyncService { void syncFromBase(ResourceSyncReq req); List getBaseTree(GetFeatureResourceTreeReq req); + + Map listFeatureCodeByFeatureResourceIds(List featureResourceIds); } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/PermissionCacheService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/PermissionCacheService.java index bb401d7c..f1608317 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/PermissionCacheService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/PermissionCacheService.java @@ -2,6 +2,13 @@ package cn.axzo.tyr.server.service; import cn.axzo.tyr.client.model.res.IdentityAuthRes; import cn.axzo.tyr.server.model.PermissionCacheKey; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Set; /** * 授权缓存服务 @@ -23,4 +30,128 @@ public interface PermissionCacheService { /** 标记缓存暂时不可用 - 等缓存全部失效 **/ void markTempDisable(PermissionCacheKey key); + + /** + * 缓存权限码跟角色的信息,采用set数据结构 + * redisKey:featureCode + * redisValue: roleId + * @param param + */ + void cachePermissionRole(CachePermissionRoleParam param); + + /** + * 根据权限码查询对应的角色信息 + * @param param + * @return + */ + List listPermissionRole(ListPermissionRoleParam param); + + /** + * 缓存权限码跟产品和单位类型的信息,采用set数据结构 + * redisKey: featureCode + * redisValue: 产品信息 + * @param param + */ + void cachePermissionProduct(CachePermissionProductParam param); + + /** + * 根据权限码查询对应的产品信息 + * 一个权限点对应的产品数据比较少,一般10多个,所以没有聚合返回,方便排查哪些权限点已经在redis中,不用从数据库中查询 + * @param param + * @return + */ + List listPermissionProduct(ListPermissionProductParam param); + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class HasPermissionRoleParam { + + private List featureCodes; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class ListPermissionRoleParam { + + private List featureCodes; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class CachePermissionRoleParam { + + private List permissionRoles; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class PermissionRole { + /** + * 权限码 + */ + private String featureCode; + + /** + * 角色id + */ + private List roleIds; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class ListPermissionProductParam { + + private List featureCodes; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class CachePermissionProductParam { + + private List permissionProducts; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class PermissionProduct { + /** + * 权限码 + */ + private String featureCode; + + /** + * 产品信息 + */ + private List products; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class Product { + /** + * 产品id + */ + private Long productId; + + /** + * 单位类型 + */ + private Integer ouType; + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/PermissionPointService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/PermissionPointService.java index 21604155..1a543d3f 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/PermissionPointService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/PermissionPointService.java @@ -1,5 +1,6 @@ package cn.axzo.tyr.server.service; +import cn.axzo.tyr.client.model.permission.DeletePermissionPointRequest; import cn.axzo.tyr.client.model.permission.PermissionPointDTO; import cn.axzo.tyr.client.model.permission.PermissionPointListQueryRequest; import cn.axzo.tyr.client.model.permission.PermissionPointMoveRequest; @@ -42,6 +43,9 @@ public interface PermissionPointService { /** 删除权限点 返回business_no **/ List delete(Long permissionId); + /** 删除权限点 返回business_no **/ + List deleteV2(DeletePermissionPointRequest request); + /** 位置移动-父级和排序 **/ void move(PermissionPointMoveRequest request); diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/PermissionQueryService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/PermissionQueryService.java index 3beccde5..82dfa740 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/PermissionQueryService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/PermissionQueryService.java @@ -4,7 +4,13 @@ import cn.axzo.tyr.client.model.req.NavTreeReq; import cn.axzo.tyr.client.model.req.PagePermissionReq; import cn.axzo.tyr.client.model.req.PagePermissionResp; import cn.axzo.tyr.client.model.req.PermissionCheckReq; +import cn.axzo.tyr.client.model.req.TreePermissionReq; +import cn.axzo.tyr.client.model.req.TreeProductFeatureResourceReq; +import cn.axzo.tyr.client.model.res.FeatureResourceDTO; import cn.axzo.tyr.client.model.res.NavTreeResp; +import cn.axzo.tyr.client.model.res.ProductFeatureResourceResp; +import cn.axzo.tyr.client.model.res.TreePermissionResp; +import cn.axzo.tyr.server.repository.entity.SaasFeatureResource; import java.util.List; @@ -18,9 +24,31 @@ import java.util.List; public interface PermissionQueryService { /** 获取导航菜单页面 **/ + @Deprecated List getNavTree(NavTreeReq req); boolean hasPermission(PermissionCheckReq req); List getPagePermission(PagePermissionReq req); + + /** + * 查询租户的权限树 + * @param request + * @return + */ + List treeProduct(TreeProductFeatureResourceReq request); + + /** + * 查询租户的权限点list + * @param request + * @return + */ + List listFeatureResource(TreeProductFeatureResourceReq request); + + /** + * 查询人的权限点树 + * @param req + * @return + */ + List treePermission(TreePermissionReq req); } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductFeatureRelationService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductFeatureRelationService.java index 30f76fde..d0c0b630 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductFeatureRelationService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductFeatureRelationService.java @@ -1,11 +1,15 @@ package cn.axzo.tyr.server.service; +import cn.axzo.foundation.page.PageResp; import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.tyr.client.model.product.OldUpdateFeatureRelationRequestV2; import cn.axzo.tyr.client.model.product.ProductFeatureRelationSearchReq; import cn.axzo.tyr.client.model.product.ProductFeatureRelationUpdateReq; import cn.axzo.tyr.client.model.product.ProductFeatureRelationVO; +import cn.axzo.tyr.client.model.req.PageProductFeatureRelationReq; import cn.axzo.tyr.server.repository.entity.ProductFeatureQuery; import cn.axzo.tyr.server.repository.entity.SaasProductModuleFeatureRelation; +import com.baomidou.mybatisplus.extension.service.IService; import java.util.List; import java.util.Map; @@ -17,11 +21,22 @@ import java.util.Set; * @author wangli * @since 2023/9/7 14:26 */ -public interface ProductFeatureRelationService { +public interface ProductFeatureRelationService extends IService { ApiResult> featureList(ProductFeatureRelationSearchReq req); ApiResult updateFeatureRelation(List req); + ApiResult updateFeatureRelationV2(OldUpdateFeatureRelationRequestV2 request); + + /** + * 更新产品功能权限关系,仅支持saas_feature_resource功能点 + * + * @param req + */ + void updateFeatureResourceRelation(List req, int relationType); + + void removeFeatureResourceRelationByProductIdAndType(Long productId, int relationType); + ApiResult> featureListByProduct(List productIds); /** @@ -37,4 +52,16 @@ public interface ProductFeatureRelationService { boolean refreshFeature(Integer workspaceType, Long productId); void removeByProductId(Long productId); + + /** + * 新增、编辑产品时做新老产品判断时使用 + * + * @param productId + * @return + */ + SaasProductModuleFeatureRelation getOneByProductId(Long productId); + + PageResp page(PageProductFeatureRelationReq param); + + List list(PageProductFeatureRelationReq param); } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductPermissionCacheService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductPermissionCacheService.java new file mode 100644 index 00000000..bdad9d38 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductPermissionCacheService.java @@ -0,0 +1,68 @@ +package cn.axzo.tyr.server.service; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +public interface ProductPermissionCacheService { + + List list(ListProductPermissionParam param); + + void store(StoreProductPermissionParam param); + + + List hasProductIds(HasProductPermissionParam param); + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class HasProductPermissionParam { + private List productIds; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class StoreProductPermissionParam { + private List productPermissions; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class ProductPermission { + private Long productId; + + private List permissions; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class ListProductPermissionParam { + private List productIds; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class PermissionDTO { + + /** + * 产品关联的字典 Code 原值 + */ + private String dictCode; + + private Long featureId; + + private String featureCode; + } +} \ No newline at end of file diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductService.java index df47aaf6..9ea8aaf1 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/ProductService.java @@ -7,6 +7,10 @@ import cn.axzo.tyr.client.model.product.ProductSearchListReq; import cn.axzo.tyr.client.model.product.ProductSearchPageReq; import cn.axzo.tyr.client.model.product.ProductUpdateReq; import cn.axzo.tyr.client.model.product.ProductVO; +import cn.axzo.tyr.client.model.req.ProductSaveReq; +import cn.axzo.tyr.client.model.req.UpdateProductStatusReq; +import cn.axzo.tyr.client.model.res.GovernmentTerminalResp; +import cn.axzo.tyr.client.model.res.WorkspaceProductResp; import java.util.List; @@ -22,11 +26,19 @@ public interface ProductService { ApiPageResult page(ProductSearchPageReq req); - ApiResult getById(Long id); + ApiResult getById(Long id, Boolean queryFeatureScope); ApiResult add(ProductAddReq req); ApiResult update(ProductUpdateReq req); ApiResult delete(Long id); + + ApiResult updateStatus(UpdateProductStatusReq req); + + ApiResult saveOrUpdate(ProductSaveReq req); + + ApiResult> getGovernmentTerminal(String terminal); + + ApiResult getWorkspaceProduct(String workspaceType); } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/RolePermissionCacheService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/RolePermissionCacheService.java new file mode 100644 index 00000000..e974b1a5 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/RolePermissionCacheService.java @@ -0,0 +1,84 @@ +package cn.axzo.tyr.server.service; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Set; + +public interface RolePermissionCacheService { + + + List list(ListRolePermissionParam param); + + /** + * redisKey:roleId + * redisValue:permission + * @param param + */ + void store(StoreRolePermissionParam param); + + List hasRoleIds(HasRolePermissionParam param); + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class HasRolePermissionParam { + private List roleIds; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class StoreRolePermissionParam { + private List rolePermissions; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class RolePermission { + private Long roleId; + + private List permissions; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class ListRolePermissionParam { + private List roleIds; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class RolePermissionDTO { + private Long roleId; + + private List permissions; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class PermissionDTO { + + private Long featureId; + + private String featureCode; + + /** + * 资源所属端 + */ + private String terminal; + } +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/RoleService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/RoleService.java index a8b18798..bf464d0d 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/RoleService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/RoleService.java @@ -1,11 +1,13 @@ package cn.axzo.tyr.server.service; +import cn.axzo.foundation.dao.support.wrapper.CriteriaField; +import cn.axzo.foundation.dao.support.wrapper.Operator; +import cn.axzo.foundation.page.IPageReq; import cn.axzo.framework.domain.page.PageResp; -import cn.axzo.pokonyan.dao.page.IPageParam; -import cn.axzo.pokonyan.dao.wrapper.CriteriaField; -import cn.axzo.pokonyan.dao.wrapper.Operator; +import cn.axzo.tyr.client.common.enums.FeatureResourceType; import cn.axzo.tyr.client.model.enums.IdentityType; import cn.axzo.tyr.client.model.req.ChangeGroupLeaderRoleReq; +import cn.axzo.tyr.client.model.req.FeatureIdPair; import cn.axzo.tyr.client.model.req.FeatureRoleRelationReq; import cn.axzo.tyr.client.model.req.QueryByIdentityIdTypeReq; import cn.axzo.tyr.client.model.req.QueryRoleByNameReq; @@ -24,10 +26,9 @@ import cn.axzo.tyr.client.model.vo.SaasRoleGroupCodeVO; import cn.axzo.tyr.client.model.vo.SaasRoleVO; import cn.axzo.tyr.client.model.vo.SaveOrUpdateRoleVO; import cn.axzo.tyr.server.model.RoleWithFeature; +import cn.axzo.tyr.server.repository.entity.SaasFeature; import cn.axzo.tyr.server.repository.entity.SaasRole; import cn.axzo.tyr.server.repository.entity.SaasRoleWithUser; -import cn.axzo.tyr.server.service.impl.TyrSaasAuthServiceImpl; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import lombok.AllArgsConstructor; import lombok.Data; @@ -65,17 +66,6 @@ public interface RoleService extends IService { List listRoleUserByPermissionGroup(List permissionGroupIdList, Set workspaceIds); - - /** - * * 过滤角色的按钮权限 - * * 1.分组上的适用单位类型过滤 - * * 2.角色的例外过滤 - * @param role - * @param userRoleInfoMap - * @return 按钮级别权限点ID - */ - Set filterPermissionPoint(Set role, TyrSaasAuthServiceImpl.OUWRoleInfo userRoleInfoMap); - /** * 通过角色名字获取角色信息 * @param req @@ -86,17 +76,6 @@ public interface RoleService extends IService { /** 分页查询角色含用户 **/ PageResp queryRoleWithUser(RoleWithUserQueryReq req); - /** - * 通过角色类型获取角色 - * @param req - * @param roleTypes - * @return - */ - List queryRoleByRoleTypes(QueryByIdentityIdTypeReq req, List roleTypes); - - List listForOUWorkspace(Long ouId, Long workspaceId, Integer workspaceJoinType); - - List queryInitRoleByWorkspaceId(String workspaceType); /** @@ -105,8 +84,6 @@ public interface RoleService extends IService { */ void deleteRole(DeleteRoleVO deleteRoleParam); - List queryRoleByFeatures(Set matchedFeatureIds); - List getByIds(Set ids); List queryByCategoryCode(List categoryCodes); @@ -120,12 +97,14 @@ public interface RoleService extends IService { List list(ListSaasRoleParam param); - Page page(PageSaasRoleParam param); + cn.axzo.foundation.page.PageResp page(PageSaasRoleParam param); void saveOrUpdateFeatureRoleRelation(List req, Long operatorId); FeatureRoleRelationResp queryFeatureRoleRelation(Long featureId); + List validFeature(List featureIds); + @SuperBuilder @Data @NoArgsConstructor @@ -159,18 +138,58 @@ public interface RoleService extends IService { @CriteriaField(field = "id", operator = Operator.NE) private Long idNE; + /** + * 权限点从saas_feature_resource表查询 + */ @CriteriaField(ignore = true) private Boolean needPermission; @CriteriaField(ignore = true) private Boolean needRoleGroup; + + @CriteriaField(ignore = true) + private Boolean needRoleUser; + + /** + * 当前非oms和政务端的权限存储在saas_feature + * 权限点从saas_feature表查询 + */ + @CriteriaField(ignore = true) + private Boolean needPermissionOld; + + /** + * 根据权限点id过滤 + */ + @CriteriaField(ignore = true) + private List featureIds; + + @CriteriaField(ignore = true) + private Boolean needPermissionRelation; + + /** + * 查询菜单树节点类型 + */ + @CriteriaField(ignore = true) + private List featureResourceTypes; + + /** + * 新旧权限点,needPermissionRelation = true时最好带上,因为新旧权限点会有冲突的情况发生 + */ + @CriteriaField(ignore = true) + private Integer type; + + /** + * 端,查询权限点时,会根据端过滤,增加效率,目前只有CMS端的新版本才冗余了端 + */ + @CriteriaField(ignore = true) + private String terminal; } @SuperBuilder @Data @NoArgsConstructor @AllArgsConstructor - class PageSaasRoleParam extends ListSaasRoleParam implements IPageParam { + class PageSaasRoleParam extends ListSaasRoleParam implements IPageReq { @CriteriaField(ignore = true) Integer page; diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasFeatureResourceService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasFeatureResourceService.java index ec83e928..1e10c7c4 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasFeatureResourceService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasFeatureResourceService.java @@ -1,12 +1,16 @@ package cn.axzo.tyr.server.service; +import cn.axzo.foundation.page.PageResp; +import cn.axzo.tyr.client.model.req.DeleteFeatureResourceReq; +import cn.axzo.tyr.client.model.req.FeatureResourceTreeSaveReq; import cn.axzo.tyr.client.model.req.GetFeatureResourceTreeReq; +import cn.axzo.tyr.client.model.req.PageSaasFeatureResourceReq; import cn.axzo.tyr.client.model.res.FeatureResourceTreeNode; +import cn.axzo.tyr.client.model.res.SaasFeatureResourceResp; import cn.axzo.tyr.server.model.ResourcePermission; import cn.axzo.tyr.server.model.ResourcePermissionQueryDTO; import cn.axzo.tyr.server.repository.entity.SaasFeatureResource; -import cn.axzo.tyr.client.model.req.FeatureResourceTreeSaveReq; -import io.swagger.models.auth.In; +import com.baomidou.mybatisplus.extension.service.IService; import java.util.List; import java.util.Set; @@ -18,22 +22,19 @@ import java.util.Set; * @author: ZhanSiHu * @date: 2024/4/3 10:17 */ -public interface SaasFeatureResourceService { +public interface SaasFeatureResourceService extends IService { Long saveOrUpdateMenu(FeatureResourceTreeSaveReq req); void updateFeatureAuthType(Long featureId, Integer authType); /**递归的**/ - List listDescendant(Long featureId); + List batchListDescendant(List featureIds); SaasFeatureResource featureResourceById(Long featureId); FeatureResourceTreeNode getTreeFeatureDescendant(Long featureId, Integer featureType); - /**删除指定菜单**/ - void deleteMenuFeature(Long featureId, Long operatorId); - /**菜单重排序**/ void reorderMenuFeature(Long featureId, Integer offset); @@ -50,5 +51,12 @@ public interface SaasFeatureResourceService { Set listAuthFree(); - List listNavMenu(String terminal); + List listByParentIdAndTerminalAndIds(Long parentId, String terminal, List featureIds); + + List list(PageSaasFeatureResourceReq param); + + PageResp page(PageSaasFeatureResourceReq param); + + void deleteFeatureResource(DeleteFeatureResourceReq param); + } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasPageElementFeatureResourceRelationService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasPageElementFeatureResourceRelationService.java new file mode 100644 index 00000000..51a9e2f9 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasPageElementFeatureResourceRelationService.java @@ -0,0 +1,15 @@ +package cn.axzo.tyr.server.service; + +import cn.axzo.foundation.page.PageResp; +import cn.axzo.tyr.client.model.req.PageElementFeatureResourceRelationReq; +import cn.axzo.tyr.server.repository.entity.SaasPageElementFeatureResourceRelation; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +public interface SaasPageElementFeatureResourceRelationService extends IService { + + List list(PageElementFeatureResourceRelationReq param); + + PageResp page(PageElementFeatureResourceRelationReq param); +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasPageElementService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasPageElementService.java new file mode 100644 index 00000000..3c31fc6d --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasPageElementService.java @@ -0,0 +1,73 @@ +package cn.axzo.tyr.server.service; + +import cn.axzo.framework.domain.page.PageResp; +import cn.axzo.tyr.client.model.req.*; +import cn.axzo.tyr.client.model.res.GetUserHasPermissionPageElementResp; +import cn.axzo.tyr.client.model.res.PageElementBasicDTO; +import cn.axzo.tyr.client.model.res.PageElementResp; + +import java.util.List; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/6/18 + */ +public interface SaasPageElementService { + + /** + * 页面元素上报 + * + * @param request 上报元素列表 + */ + void report(PageElementReportReq request); + + /** + * 查询页面已配置的元素 + * + * @param request + * @return + */ + List getPageElement(GetPageElementReq request); + + /** + * 通过组件code查询页面元素 + * + * @param terminal 组件所属端 + * @param featureResourceUniCodes 组件code + * @return + */ + List getByTerminalAndUniCodes(String terminal, List featureResourceUniCodes); + + /** + * 新增页面组件与页面元素关系 + * + * @param modifyPageElementRelation 关系对象 + */ + void modifyPageElementRelation(ModifyPageElementRelationDTO modifyPageElementRelation); + + /** + * 通过组件code删除绑定关系 + * + * @param terminal 组件所属端 + * @param featureResourceUniCodes 组件code + * @param operatorId 操作人 + */ + void deleteRelationByTerminalAndUniCodes(String terminal, List featureResourceUniCodes, Long operatorId); + + /** + * 分页查询页面元素接口 + * + * @param request 查询条件 + * @return + */ + PageResp page(PageQueryElementReq request); + + /** + * 根据用户传入的页面code,查询用户有权限的元素code + * + * @param request + * @return + */ + GetUserHasPermissionPageElementResp getUserHasPermissionPageElement(GetUserHasPermissionPageElementReq request); +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasPgroupPermissionRelationOperateLogService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasPgroupPermissionRelationOperateLogService.java new file mode 100644 index 00000000..5f2c192b --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasPgroupPermissionRelationOperateLogService.java @@ -0,0 +1,21 @@ +package cn.axzo.tyr.server.service; + +import cn.axzo.tyr.client.model.req.PermissionOperateLogReq; +import cn.axzo.tyr.server.model.BasicRoleDO; +import cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelationOperateLog; + +import java.util.List; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/7/24 + */ +public interface SaasPgroupPermissionRelationOperateLogService { + + void batchSave(List logs); + + List getPersonBasicRoles(Long personId); + + void save(PermissionOperateLogReq req); +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasPgroupPermissionRelationService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasPgroupPermissionRelationService.java index 21ecc4e7..4927926f 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasPgroupPermissionRelationService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasPgroupPermissionRelationService.java @@ -1,11 +1,64 @@ package cn.axzo.tyr.server.service; +import cn.axzo.foundation.page.PageResp; +import cn.axzo.tyr.client.model.req.PagePgroupPermissionRelationReq; +import cn.axzo.tyr.client.model.vo.SaveOrUpdateRoleVO; import cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation; +import com.baomidou.mybatisplus.extension.service.IService; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; import java.util.List; -public interface SaasPgroupPermissionRelationService { - void saveOrUpdate(List groupIds, List relations); +public interface SaasPgroupPermissionRelationService extends IService { + void saveOrUpdate(UpsertPermissionRelationParam param); - void deleteByFeatureIds(List featureIds); + PageResp page(PagePgroupPermissionRelationReq param); + + List list(PagePgroupPermissionRelationReq param); + + void delete(DeleteParam param); + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class UpsertPermissionRelationParam { + + /** + * 权限组id + */ + private List groupIds; + + /** + * 权限关系明细 + */ + private List relations; + + /** + * 同一个端更新只能是一个版本类型 + * 关联类型(0:saas_feature,1:saas_feature_resource) + */ + private Integer type; + + /** + * 操作人ID + */ + private Long operatorId; + + /** + * 请求参数 + */ + private SaveOrUpdateRoleVO requestData; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class DeleteParam { + private List ids; + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasRoleGroupRelationService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasRoleGroupRelationService.java index 4579b5ff..a2813b04 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasRoleGroupRelationService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasRoleGroupRelationService.java @@ -1,10 +1,10 @@ package cn.axzo.tyr.server.service; -import cn.axzo.pokonyan.dao.page.IPageParam; -import cn.axzo.pokonyan.dao.wrapper.CriteriaField; -import cn.axzo.pokonyan.dao.wrapper.Operator; +import cn.axzo.foundation.dao.support.wrapper.CriteriaField; +import cn.axzo.foundation.dao.support.wrapper.Operator; +import cn.axzo.foundation.page.IPageReq; +import cn.axzo.foundation.page.PageResp; import cn.axzo.tyr.server.repository.entity.SaasRoleGroupRelation; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import lombok.AllArgsConstructor; import lombok.Data; @@ -18,7 +18,7 @@ public interface SaasRoleGroupRelationService extends IService list(ListSaasRoleGroupRelationParam param); - Page page(PageSaasRoleGroupRelationParam param); + PageResp page(PageSaasRoleGroupRelationParam param); @SuperBuilder @Data @@ -38,7 +38,7 @@ public interface SaasRoleGroupRelationService extends IService { void delete(List ids); + void deleteV2(SaasRoleGroupDeleteRequest request); + /** * 分组CODE查询角色分组 * @param categoryCode @@ -41,4 +48,9 @@ public interface SaasRoleGroupService extends IService { * @param type 1-仅查当前code 2-对应code角色组及子级角色组 3-仅对应code角色组的子级 * **/ List listByCodes(List codes, int type); + + List list(ListSaasRoleGroupParam param); + + PageResp page(PageSaasRoleGroupParam param); + } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasRoleUserRelationService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasRoleUserRelationService.java index b757d02d..0d24c505 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasRoleUserRelationService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasRoleUserRelationService.java @@ -2,7 +2,14 @@ package cn.axzo.tyr.server.service; import cn.axzo.framework.domain.page.PageResp; import cn.axzo.tyr.client.model.roleuser.dto.SaasRoleUserDTO; +import cn.axzo.tyr.client.model.roleuser.dto.SaasRoleUserV2DTO; +import cn.axzo.tyr.client.model.roleuser.req.ListRoleUserRelationParam; +import cn.axzo.tyr.client.model.roleuser.req.PageRoleUserRelationParam; import cn.axzo.tyr.client.model.roleuser.req.RoleUserParam; +import cn.axzo.tyr.server.repository.entity.SaasRole; +import cn.axzo.tyr.server.repository.entity.SaasRoleUserRelation; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; import java.util.List; @@ -10,8 +17,12 @@ import java.util.List; * @author haiyangjin * @date 2023/9/14 */ -public interface SaasRoleUserRelationService { +public interface SaasRoleUserRelationService extends IService { List list(RoleUserParam param); PageResp pageQuery(RoleUserParam param); + + List listV2(ListRoleUserRelationParam param); + + cn.axzo.foundation.page.PageResp page(PageRoleUserRelationParam param); } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasRoleUserService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasRoleUserService.java index 60fae1fb..1c2d3cc1 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasRoleUserService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/SaasRoleUserService.java @@ -1,7 +1,9 @@ package cn.axzo.tyr.server.service; import cn.axzo.tyr.client.model.enums.IdentityType; +import cn.axzo.tyr.client.model.roleuser.RoleUserUpdateReq; import cn.axzo.tyr.client.model.roleuser.dto.GetUserAutoOwnRoleResp; +import cn.axzo.tyr.client.model.roleuser.dto.GetUserFeatureResourceIdsResp; import cn.axzo.tyr.client.model.roleuser.dto.SuperAminInfoResp; import cn.axzo.tyr.client.model.roleuser.req.*; import cn.axzo.tyr.server.repository.entity.SaasRoleUserRelation; @@ -97,4 +99,19 @@ public interface SaasRoleUserService { * @return */ GetUserAutoOwnRoleResp getUserAutoOwnRole(GetUserAutoOwnRoleReq req); + + /** + * 查询用户已有的功能资源 + * + * @param req + * @return + */ + GetUserFeatureResourceIdsResp getUserFeatureResourceIds(GetUserFeatureResourceIdsReq req); + + /** + * 根据id更新saas_role_user_relation数据 + * @param roleUserUpdateReqs 请求参数 + * @return + */ + Boolean batchUpdateById(List roleUserUpdateReqs); } \ No newline at end of file diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/TyrSaasAuthService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/TyrSaasAuthService.java index 69137c8b..81ccc0b3 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/TyrSaasAuthService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/TyrSaasAuthService.java @@ -8,6 +8,7 @@ import cn.axzo.tyr.client.model.req.ListIdentityFromPermissionReq; import cn.axzo.tyr.client.model.req.ListPermissionFromFeatureReq; import cn.axzo.tyr.client.model.req.ListPermissionFromIdentityReq; import cn.axzo.tyr.client.model.req.ListPermissionFromRoleGroupReq; +import cn.axzo.tyr.client.model.req.PermissionCheckReq; import cn.axzo.tyr.client.model.req.WorkspacePermissionIdentityReq; import cn.axzo.tyr.client.model.res.IdentityAuthRes; import cn.axzo.tyr.client.model.res.ListIdentityFromPermissionResp; @@ -51,4 +52,18 @@ public interface TyrSaasAuthService { * @return */ List listAuthByResourceAndRoleGroup(ListPermissionFromRoleGroupReq listPermissionFromRoleGroupReq); + + /** + * 基于saas_feature的接口鉴权 + * @param req + * @return + */ + boolean authPermission(PermissionCheckReq req); + + /** + * 基于saas_feature_resource的接口鉴权 + * @param req + * @return + */ + boolean authNewPermission(PermissionCheckReq req); } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/WorkspaceProductService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/WorkspaceProductService.java new file mode 100644 index 00000000..c3d4e4f1 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/WorkspaceProductService.java @@ -0,0 +1,114 @@ +package cn.axzo.tyr.server.service; + +import cn.axzo.tyr.client.common.enums.FeatureResourceType; +import cn.axzo.tyr.client.model.req.FeatureIdPair; +import cn.axzo.tyr.server.repository.entity.SaasProductModuleFeatureRelation; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Set; + +public interface WorkspaceProductService { + + + List listWorkspaceProduct(WorkspaceProductParam param); + + /** + * 从缓存中查询项目的产品 + * @param param + * @return + */ + List listWorkspaceProductCached(WorkspaceProductParam param); + + /** + * 缓存项目的产品 + * @param param + */ + void storeWorkspaceProduct(StoreWorkspaceProductParam param); + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class WorkspaceProductParam { + /** + * 端 + */ + private String terminal; + + /** + * 项目id + */ + private Set workspaceIds; + + private Integer type; + + /** + * 查询菜单树节点类型 + */ + private List featureResourceTypes; + + /** + * 因为存在一个租户不同版本的菜单资源 + */ + private List featureIdPairs; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class WorkspaceProduct { + + /** + * 项目id + */ + private Long workspaceId; + + /** + * 产品资源树关系 + */ + private List saasProductModuleFeatureRelations; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class StoreWorkspaceProductParam { + + private List workspaceProducts; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class WorkspaceProductDTO { + /** + * 项目id + */ + private Long workspaceId; + + /** + * 产品id + */ + private Set productIds; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + class ListWorkspaceProductParam { + + /** + * 项目id + */ + private Set workspaceIds; + } +} + diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/FeatureResourceSyncServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/FeatureResourceSyncServiceImpl.java index a6da2b19..a5544540 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/FeatureResourceSyncServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/FeatureResourceSyncServiceImpl.java @@ -1,31 +1,50 @@ package cn.axzo.tyr.server.service.impl; import cn.axzo.basics.common.BeanMapper; +import cn.axzo.basics.common.constant.enums.DeleteEnum; import cn.axzo.basics.common.util.TreeUtil; +import cn.axzo.basics.profiles.api.UserProfileServiceApi; +import cn.axzo.basics.profiles.dto.basic.PersonProfileDto; +import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; import cn.axzo.tyr.client.common.enums.FeatureResourceType; +import cn.axzo.tyr.client.common.enums.PermissionRelationOperateLogSceneEnum; import cn.axzo.tyr.client.model.req.GetFeatureResourceTreeReq; import cn.axzo.tyr.client.model.req.ResourceSyncReq; import cn.axzo.tyr.client.model.res.FeatureResourceTreeNode; import cn.axzo.tyr.server.inner.feign.BaseFeatureResourceApi; +import cn.axzo.tyr.server.model.RelationOperateLogResourceBindRoleDO; import cn.axzo.tyr.server.repository.dao.SaasFeatureResourceDao; -import cn.axzo.tyr.server.repository.entity.SaasFeatureResource; +import cn.axzo.tyr.server.repository.dao.SaasPageElementFeatureResourceRelationDao; +import cn.axzo.tyr.server.repository.dao.SaasPgroupPermissionRelationDao; +import cn.axzo.tyr.server.repository.dao.SaasPgroupRoleRelationDao; +import cn.axzo.tyr.server.repository.dao.SaasRoleDao; +import cn.axzo.tyr.server.repository.entity.*; import cn.axzo.tyr.server.service.FeatureResourceSyncService; +import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationOperateLogService; import cn.axzo.tyr.server.util.RpcInternalUtil; -import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.util.StrUtil; +import cn.azxo.framework.common.constatns.Constants; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.MDC; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.NEW_FEATURE; /** * 功能资源同步服务实现 @@ -45,12 +64,25 @@ public class FeatureResourceSyncServiceImpl implements FeatureResourceSyncServic private final SaasFeatureResourceCacheService saasFeatureResourceCacheService; + private final SaasRoleDao saasRoleDao; + + private final SaasPgroupRoleRelationDao saasPgroupRoleRelationDao; + + private final SaasPgroupPermissionRelationDao saasPgroupPermissionRelationDao; + + private final SaasPageElementFeatureResourceRelationDao saasPageElementFeatureResourceRelationDao; + + private final SaasPgroupPermissionRelationOperateLogService saasPgroupPermissionRelationOperateLogService; + private final UserProfileServiceApi userProfileServiceApi; + @Qualifier("asyncExecutor") @Autowired private ExecutorService asyncExecutor; @Override public List getSyncTreeById(Long id) { + List allFeatureResourceIds = Lists.newArrayList(id); + //选中同步的数据 SaasFeatureResource resource = featureResourceDao.lambdaQuery() .eq(SaasFeatureResource::getId, id) @@ -62,6 +94,7 @@ public class FeatureResourceSyncServiceImpl implements FeatureResourceSyncServic List resourceList = featureResourceDao.lambdaQuery() .in(SaasFeatureResource::getId, resource.splitPath()) .list(); + allFeatureResourceIds.addAll(CollectionUtils.emptyIfNull(resourceList).stream().map(BaseEntity::getId).collect(Collectors.toList())); //resourceList.add(resource); //如果是页面或应用入口-同时返回所有页面组件 if (FeatureResourceType.applyPage(resource.getFeatureType())) { @@ -69,15 +102,20 @@ public class FeatureResourceSyncServiceImpl implements FeatureResourceSyncServic .eq(SaasFeatureResource::getFeatureType, FeatureResourceType.COMPONENT.getCode()) .apply("FIND_IN_SET(" + id + ", path)") .list(); + allFeatureResourceIds.addAll(CollectionUtils.emptyIfNull(componentList).stream().map(BaseEntity::getId).collect(Collectors.toList())); resourceList.addAll(componentList); } + Map> featureResourceRoleCodeMap = getFeatureResourceRoleCodeMap(allFeatureResourceIds); + List dtoList = BeanMapper.copyList(resourceList, FeatureResourceTreeNode.class); + dtoList.forEach(e -> e.setRoleCodes(featureResourceRoleCodeMap.get(e.getId()))); return TreeUtil.buildTree(dtoList); } @Override @Transactional(rollbackFor = Exception.class) public void syncFromBase(ResourceSyncReq req) { + req.setTraceId(MDC.get(Constants.CTX_LOG_ID_MDC)); if (req.getIds().size() > 1) { //超过一个异步处理 CompletableFuture.runAsync(() -> doSyncFromBase(req), asyncExecutor).whenComplete((t, ex) -> saasFeatureResourceCacheService.clearCache()); @@ -88,15 +126,27 @@ public class FeatureResourceSyncServiceImpl implements FeatureResourceSyncServic } private void doSyncFromBase(ResourceSyncReq req) { + List operateDos = Lists.newCopyOnWriteArrayList(); //TODO:@Zhan 并发处理;同一个parent下同批查询 //处理数据缓存:避免同级节点上级重复处理 - 上级code查询 final Map codeCache = new ConcurrentHashMap<>(); + Set syncedRoleFeatureResourceIds = Sets.newConcurrentHashSet(); + try { + for (Long id : req.getIds()) { + //获取基准环境配置数据:同步某个ID的数据 需要同步处理它所有上级及下级组件 + List syncList = RpcInternalUtil.rpcProcessor(() -> baseFeatureResourceApi.getSyncTreeById(id), + "get base sync tree by id", id).getData(); + syncResourceProcess(syncList, codeCache, req.getOperatorId(), operateDos, syncedRoleFeatureResourceIds); + } + } catch (Exception e) { + log.warn("do sync from base error", e); + } - for (Long id : req.getIds()) { - //获取基准环境配置数据:同步某个ID的数据 需要同步处理它所有上级及下级组件 - List syncList = RpcInternalUtil.rpcProcessor(() -> baseFeatureResourceApi.getSyncTreeById(id), - "get base sync tree by id", id).getData(); - syncResourceProcess(syncList, codeCache, req.getOperatorId()); + // 记录操作日志 + try { + saveOperateLog(req, operateDos); + } catch (Exception e) { + log.warn("save operate log error", e); } } @@ -106,91 +156,246 @@ public class FeatureResourceSyncServiceImpl implements FeatureResourceSyncServic "get resource tree from base env", req).getData(); } - private void syncResourceProcess(List syncList, Map codeCache, Long operatorId) { + @Override + public Map listFeatureCodeByFeatureResourceIds(List featureResourceIds) { + if (CollectionUtils.isEmpty(featureResourceIds)) { + return Maps.newHashMap(); + } + List resources = featureResourceDao.lambdaQuery() + .in(SaasFeatureResource::getId, featureResourceIds) + .list(); + if (CollectionUtils.isEmpty(resources)) { + return Maps.newHashMap(); + } + Map relationMap = saasPageElementFeatureResourceRelationDao.listByUniCodeAndTerminal(resources.stream().map(SaasFeatureResource::getUniCode).distinct().collect(Collectors.toList()), + resources.get(0).getTerminal(), null).stream().collect(Collectors.toMap(SaasPageElementFeatureResourceRelation::getFeatureResourceUniCode, Function.identity(), (v1, v2) -> v1)); + + Map featureResourceIdCodeMap = Maps.newHashMap(); + resources.forEach(e -> { + SaasPageElementFeatureResourceRelation relation = relationMap.get(e.getUniCode()); + if (Objects.nonNull(relation)) { + featureResourceIdCodeMap.put(e.getId(), relation.getPageElementCode()); + } + }); + return featureResourceIdCodeMap; + } + + private void syncResourceProcess(List syncList, Map codeCache, Long operatorId, + List operateDos, Set syncedRoleFeatureResourceIds) { for (FeatureResourceTreeNode treeNode : syncList) { if (codeCache.containsKey(treeNode.getId())) { //已处理过 log.info("already sync resource:{}", treeNode.getId()); //递归子节点 - if (CollectionUtil.isNotEmpty(treeNode.getChildren())) { - syncResourceProcess(treeNode.getChildren(), codeCache, operatorId); + if (CollectionUtils.isNotEmpty(treeNode.getChildren())) { + syncResourceProcess(treeNode.getChildren(), codeCache, operatorId, operateDos, syncedRoleFeatureResourceIds); } continue; } //缓存code - codeCache.put(treeNode.getId(), treeNode.getFeatureCode()); + codeCache.put(treeNode.getId(), treeNode.getUniCode()); SaasFeatureResource baseResource = BeanMapper.copyBean(treeNode, SaasFeatureResource.class); //修正数据 - String parentCode = codeCache.get(baseResource.getParentId()); - fixData(baseResource, parentCode); + String parentUniCode = codeCache.get(baseResource.getParentId()); + SaasFeatureResource parent = StringUtils.isBlank(parentUniCode) ? null : featureResourceDao.lambdaQuery() + .eq(SaasFeatureResource::getUniCode, parentUniCode) + .one(); - SaasFeatureResource resource = featureResourceDao.getByCode(treeNode.getFeatureCode()); + SaasFeatureResource resource = featureResourceDao.lambdaQuery() + .eq(SaasFeatureResource::getUniCode, treeNode.getUniCode()) + .one(); if (resource == null) { //新增 baseResource.setCreateBy(operatorId); baseResource.setUpdateBy(operatorId); - newResource(baseResource); + newResource(baseResource, parent); } else { //更新 - 恢复不能变更的数据 baseResource.setId(resource.getId()); - if (baseResource.getParentId() < 0) { - baseResource.setPath(resource.getId().toString()); - } else { - baseResource.setPath(baseResource.getPath() + "," + resource.getId()); - } + + // 当前节点是父节点 + resolveParentAndPath(baseResource, parent); + baseResource.setUpdateBy(operatorId); baseResource.setAppItemId(resource.getAppItemId()); featureResourceDao.updateById(baseResource); - - if (!StrUtil.equals(baseResource.getPath(), resource.getPath())) { - //层级变化 - log.info("replace path from old:{} to new:{}", resource.getPath(), baseResource.getPath()); - featureResourceDao.replacePath(resource.getPath(), baseResource.getPath()); - } } + + // 处理资源关联的权限 + RelationOperateLogResourceBindRoleDO logResourceBindRoleDO = doFeatureResourceRole(baseResource, treeNode.getRoleCodes(), operatorId, syncedRoleFeatureResourceIds); + if (Objects.nonNull(logResourceBindRoleDO)) { + operateDos.add(logResourceBindRoleDO); + } + //递归子节点 - if (CollectionUtil.isNotEmpty(treeNode.getChildren())) { - syncResourceProcess(treeNode.getChildren(), codeCache, operatorId); + if (CollectionUtils.isNotEmpty(treeNode.getChildren())) { + syncResourceProcess(treeNode.getChildren(), codeCache, operatorId, operateDos, syncedRoleFeatureResourceIds); } } } - - /** 修正当前环境的数据 parentId path **/ - private void fixData(SaasFeatureResource resource, String parentCode) { - // 原数据的parentId为负数,表示是端下面的第一层数据,此数据的path为当前数据的id,parentId为原数据的parentId - if (resource.getParentId() < 0) { - resource.setParentId(resource.getParentId()); - return; - } - if (StrUtil.isBlank(parentCode)) { - resource.setParentId(0L); - resource.setPath("0"); - } else { - //找当前环境的parent - SaasFeatureResource parent = featureResourceDao.getByCode(parentCode); - if (parent == null) { - resource.setParentId(0L); - resource.setPath("0"); - } else { - resource.setParentId(parent.getId()); - resource.setPath(parent.getPath()); - } - } - } - - private void newResource(SaasFeatureResource resource) { + private void newResource(SaasFeatureResource resource, SaasFeatureResource parent) { featureResourceDao.save(resource); - //path追加自身ID - if (resource.getParentId() < 0) { - resource.setPath(resource.getId().toString()); - } else { - resource.setPath(resource.getPath() + "," + resource.getId()); - } + resolveParentAndPath(resource, parent); featureResourceDao.updateById(resource); } + + private void resolveParentAndPath(SaasFeatureResource resource, SaasFeatureResource parent) { + // 当前节点是父节点 + if (parent == null) { + resource.setParentId(0L); + resource.setPath(resource.getId().toString() + ","); + } else { + resource.setParentId(parent.getId()); + resource.setPath(parent.getPath() + resource.getId() + ","); + } + } + + private RelationOperateLogResourceBindRoleDO doFeatureResourceRole(SaasFeatureResource featureResource, List roleCodes, + Long operatorId, Set syncedRoleFeatureResourceIds) { + if (Objects.isNull(featureResource)) { + return null; + } + // 同一FeatureResource如果已经处理过角色同步,不再进行再次处理。 + if (syncedRoleFeatureResourceIds.contains(featureResource.getId())) { + log.warn("sync role bind has handled,feature resource id:{}", featureResource.getId()); + return null; + } + syncedRoleFeatureResourceIds.add(featureResource.getId()); + + // 先清除资源绑定的角色 @20240723 产品武艳华要求资源绑定的角色以PRE角色为准; + List existPermissionRelations = saasPgroupPermissionRelationDao.lambdaQuery() + .eq(BaseEntity::getIsDelete, DeleteEnum.NORMAL.value) + .eq(SaasPgroupPermissionRelation::getFeatureId, featureResource.getId()) + .eq(SaasPgroupPermissionRelation::getType, NEW_FEATURE) + .list(); + if (CollectionUtils.isNotEmpty(existPermissionRelations)) { + log.warn("sync menu delete SaasPgroupPermissionRelation operateId:{} featureResourceId:{}, saasPgroupPermissionRelationIds:{}", operatorId, featureResource.getId(), existPermissionRelations.stream().map(BaseEntity::getId).collect(Collectors.toList())); + saasPgroupPermissionRelationDao.removeByFeatureIdAndGroupIds(featureResource.getId(), existPermissionRelations.stream().map(SaasPgroupPermissionRelation::getGroupId).collect(Collectors.toList()), operatorId); + } + + // 操作日志 + RelationOperateLogResourceBindRoleDO logResourceBindRoleDO = RelationOperateLogResourceBindRoleDO.builder() + .uniCode(featureResource.getUniCode()) + .build(); + + roleCodes = CollectionUtils.emptyIfNull(roleCodes).stream().filter(StringUtils::isNotBlank).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(roleCodes)) { + return logResourceBindRoleDO; + } + +// List existRoleCodes = getFeatureResourceRoleCodeMap(Lists.newArrayList(featureResource.getId())) +// .get(featureResource.getId()); +// +// if (CollectionUtils.isNotEmpty(existRoleCodes)) { +// roleCodes.removeAll(existRoleCodes); +// } +// if (CollectionUtils.isEmpty(roleCodes)) { +// return; +// } + + List saasRoles = saasRoleDao.listByRoleCodes(roleCodes); + if (CollectionUtils.isEmpty(saasRoles)) { + return logResourceBindRoleDO; + } + List saasPgroupRoleRelations = saasPgroupRoleRelationDao.findByRoleIds(saasRoles.stream().map(BaseEntity::getId).collect(Collectors.toList())); + Set groupIds = saasPgroupRoleRelations.stream().map(SaasPgroupRoleRelation::getGroupId).collect(Collectors.toSet()); + List existRoleIds = saasPgroupRoleRelations.stream().map(SaasPgroupRoleRelation::getRoleId).collect(Collectors.toList()); + if (CollectionUtils.isNotEmpty(groupIds)) { + List insertRelation = groupIds.stream() + .map(groupId -> { + SaasPgroupPermissionRelation relation = new SaasPgroupPermissionRelation(); + relation.setFeatureId(featureResource.getId()); + relation.setGroupId(groupId); + relation.setCreateBy(operatorId); + relation.setType(NEW_FEATURE); + relation.setFeatureType(featureResource.getFeatureType()); + relation.setTerminal(featureResource.getTerminal()); + return relation; + }) + .collect(Collectors.toList()); + saasPgroupPermissionRelationDao.saveBatch(insertRelation); + + logResourceBindRoleDO.setRoleCodes(saasRoles.stream().filter(e -> existRoleIds.contains(e.getId())).map(SaasRole::getRoleCode).collect(Collectors.toList())); + } + return logResourceBindRoleDO; + } + + private Map> getFeatureResourceRoleCodeMap(List allFeatureResourceIds) { + allFeatureResourceIds = allFeatureResourceIds.stream().distinct().collect(Collectors.toList()); + List permissionRelations = saasPgroupPermissionRelationDao.lambdaQuery() + .eq(BaseEntity::getIsDelete, DeleteEnum.NORMAL.value) + .in(SaasPgroupPermissionRelation::getFeatureId, allFeatureResourceIds) + .eq(SaasPgroupPermissionRelation::getType, NEW_FEATURE) + .list(); + if (CollectionUtils.isEmpty(permissionRelations)) { + return Collections.emptyMap(); + } + + List roleRelations = saasPgroupRoleRelationDao.lambdaQuery() + .eq(BaseEntity::getIsDelete, DeleteEnum.NORMAL.value) + .in(SaasPgroupRoleRelation::getGroupId, permissionRelations.stream().map(SaasPgroupPermissionRelation::getGroupId).collect(Collectors.toSet())) + .list(); + if (CollectionUtils.isEmpty(roleRelations)) { + return Collections.emptyMap(); + } + + List roles = saasRoleDao.lambdaQuery() + .eq(BaseEntity::getIsDelete, DeleteEnum.NORMAL.value) + .in(BaseEntity::getId, roleRelations.stream().map(SaasPgroupRoleRelation::getRoleId).collect(Collectors.toSet())) + .list(); + if (CollectionUtils.isEmpty(roles)) { + return Collections.emptyMap(); + } + + Map> featureIdToGroupIdsMap = permissionRelations.stream() + .collect(Collectors.groupingBy(SaasPgroupPermissionRelation::getFeatureId, + Collectors.mapping(SaasPgroupPermissionRelation::getGroupId, Collectors.toList()))); + + Map> groupIdToRoleIdsMap = roleRelations.stream() + .collect(Collectors.groupingBy(SaasPgroupRoleRelation::getGroupId, + Collectors.mapping(SaasPgroupRoleRelation::getRoleId, Collectors.toList()))); + + Map roleIdToRoleCodeMap = Maps.newHashMap(); + for (SaasRole role : roles) { + if (StringUtils.isNotBlank(role.getRoleCode())) { + roleIdToRoleCodeMap.put(role.getId(), role.getRoleCode()); + } + } + + Map> featureRoleMap = new HashMap<>(); + featureIdToGroupIdsMap.forEach((featureId, groupIds) -> { + List roleCodes = groupIds.stream() + .flatMap(groupId -> groupIdToRoleIdsMap.getOrDefault(groupId, Collections.emptyList()).stream()) + .map(roleIdToRoleCodeMap::get) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toList()); + + featureRoleMap.put(featureId, roleCodes); + }); + + return featureRoleMap; + } + + + private void saveOperateLog(ResourceSyncReq req, List operateDos) { + List personProfileDtos = RpcInternalUtil.rpcListProcessor(() -> userProfileServiceApi.getPersonProfiles(Lists.newArrayList(req.getOperatorId())), "查询用户信息", req.getOperatorId()).getData(); + PersonProfileDto operator = CollectionUtils.isEmpty(personProfileDtos) ? null : personProfileDtos.get(0); + + SaasPgroupPermissionRelationOperateLog operateLog = SaasPgroupPermissionRelationOperateLog.builder() + .tableName(SaasPgroupPermissionRelationOperateLogServiceImpl.TABLE_NAME_FEATURE_RESOURCE) + .scene(PermissionRelationOperateLogSceneEnum.OMS_SYNC.getValue()) + .createBy(req.getOperatorId()) + .createByName(Objects.isNull(operator) ? "" : operator.getRealName()) + .traceId(req.getTraceId()) + .requestData(JSONObject.toJSONString(req)) + .operateData(JSONObject.toJSONString(operateDos)) + .createByRole(JSONObject.toJSONString(saasPgroupPermissionRelationOperateLogService.getPersonBasicRoles(req.getOperatorId()))) + .build(); + saasPgroupPermissionRelationOperateLogService.batchSave(Lists.newArrayList(operateLog)); + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionCacheServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionCacheServiceImpl.java index 43eb6d75..10f2fe8c 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionCacheServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionCacheServiceImpl.java @@ -1,20 +1,32 @@ package cn.axzo.tyr.server.service.impl; +import cn.axzo.pokonyan.config.redis.RedisClient; import cn.axzo.pokonyan.config.redis.RedisUtil; import cn.axzo.tyr.client.model.res.IdentityAuthRes; import cn.axzo.tyr.server.model.PermissionCacheKey; import cn.axzo.tyr.server.service.PermissionCacheService; import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.serializer.SerializerFeature; +import com.google.common.collect.Streams; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.dao.DataAccessException; +import org.springframework.data.redis.core.RedisOperations; +import org.springframework.data.redis.core.SessionCallback; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; /** * 授权缓存服务实现 @@ -35,7 +47,10 @@ public class PermissionCacheServiceImpl implements PermissionCacheService { /** 授权缓存过期时间 **/ @Value("${axzo.cache.auth.expire:30}") private Long expireInMinutes; - + @Autowired + protected StringRedisTemplate redisTemplate; + private static final String PERMISSION_ROLE_KEY = "permission:role:%s"; + private static final String PERMISSION_PRODUCT_KEY = "permission:product:%s"; @Override public boolean cacheDisable(PermissionCacheKey key) { @@ -95,4 +110,111 @@ public class PermissionCacheServiceImpl implements PermissionCacheService { log.error("mark permission refresh error", ex); } } + + @Override + public void cachePermissionRole(CachePermissionRoleParam param) { + redisTemplate.executePipelined(new SessionCallback() { + @Override + public Object execute(RedisOperations operations) throws DataAccessException { + + for (PermissionRole permissionRole : param.getPermissionRoles()) { + String redisKey = getKey(PERMISSION_ROLE_KEY, permissionRole.getFeatureCode()); + + String[] redisValues = permissionRole.getRoleIds().stream().toArray(String[]::new); + + RedisClient.SetOps.sAdd(redisKey, redisValues); + redisTemplate.expire(redisKey, 4, TimeUnit.DAYS); + log.info("succeed to store permission role: redisKey:{} value:{}", redisKey, redisValues); + } + return null; + } + }); + } + + @Override + public List listPermissionRole(ListPermissionRoleParam param) { + if (CollectionUtils.isEmpty(param.getFeatureCodes())) { + return Collections.emptyList(); + } + + List redisValues = redisTemplate.executePipelined(new SessionCallback() { + @Override + public Object execute(RedisOperations operations) throws DataAccessException { + + for (String featureCode : param.getFeatureCodes()) { + String redisKey = getKey(PERMISSION_ROLE_KEY, featureCode); + operations.opsForSet().members(redisKey); + } + return null; + } + }); + + return Streams.zip(param.getFeatureCodes().stream(), + redisValues.stream(), + (featureCode, redisValue) -> { + PermissionRole permissionProduct = PermissionRole.builder() + .featureCode(featureCode) + .roleIds(JSONArray.parseArray(JSONArray.toJSONString(redisValue), Long.class)) + .build(); + return permissionProduct; + }) + .collect(Collectors.toList()); + } + + @Override + public void cachePermissionProduct(CachePermissionProductParam param) { + redisTemplate.executePipelined(new SessionCallback() { + @Override + public Object execute(RedisOperations operations) throws DataAccessException { + + for (PermissionProduct permissionProduct : param.getPermissionProducts()) { + String redisKey = getKey(PERMISSION_PRODUCT_KEY, permissionProduct.getFeatureCode()); + + String[] redisValues = permissionProduct.getProducts().stream() + .map(JSONObject::toJSONString) + .toArray(String[]::new); + + RedisClient.SetOps.sAdd(redisKey, redisValues); + redisTemplate.expire(redisKey, 4, TimeUnit.DAYS); + log.info("succeed to store permission product: redisKey:{} value:{}", redisKey, redisValues); + } + return null; + } + }); + } + + @Override + public List listPermissionProduct(ListPermissionProductParam param) { + if (CollectionUtils.isEmpty(param.getFeatureCodes())) { + return Collections.emptyList(); + } + + + List redisValues = redisTemplate.executePipelined(new SessionCallback() { + @Override + public Object execute(RedisOperations operations) throws DataAccessException { + + for (String featureCode : param.getFeatureCodes()) { + String redisKey = getKey(PERMISSION_PRODUCT_KEY, featureCode); + operations.opsForSet().members(redisKey); + } + return null; + } + }); + + return Streams.zip(param.getFeatureCodes().stream(), + redisValues.stream(), + (featureCode, redisValue) -> { + PermissionProduct permissionProduct = PermissionProduct.builder() + .featureCode(featureCode) + .products(JSONArray.parseArray(JSONArray.toJSONString(redisValue), Product.class)) + .build(); + return permissionProduct; + }) + .collect(Collectors.toList()); + } + + private String getKey(String pref, Object... params) { + return String.format(pref, params); + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionGroupImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionGroupImpl.java index 39aa54fd..c3ac8143 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionGroupImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionGroupImpl.java @@ -20,6 +20,7 @@ import cn.axzo.tyr.server.repository.dao.SaasPermissionGroupScopeDao; import cn.axzo.tyr.server.repository.dao.SaasPgroupPermissionRelationDao; import cn.axzo.tyr.server.repository.dao.SaasPgroupRoleRelationDao; import cn.axzo.tyr.server.repository.dao.SaasRoleDao; +import cn.axzo.tyr.server.repository.entity.SaasFeature; import cn.axzo.tyr.server.repository.entity.SaasPermissionGroup; import cn.axzo.tyr.server.repository.entity.SaasPermissionGroupScope; import cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation; @@ -27,6 +28,7 @@ import cn.axzo.tyr.server.repository.entity.SaasPgroupRoleRelation; import cn.axzo.tyr.server.repository.entity.SaasRole; import cn.axzo.tyr.server.service.PermissionGroupService; import cn.axzo.tyr.server.service.PermissionPointService; +import cn.axzo.tyr.server.service.RoleService; import cn.axzo.tyr.server.service.SaasPermissionGroupScopeService; import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationService; import cn.hutool.core.collection.CollectionUtil; @@ -52,6 +54,8 @@ import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; +import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.OLD_FEATURE; + /** * 权限集 * @@ -71,6 +75,7 @@ public class PermissionGroupImpl implements PermissionGroupService { private final SaasPgroupPermissionRelationService saasPgroupPermissionRelationService; private final SaasRoleDao saasRoleDao; private final SaasPermissionGroupScopeService saasPermissionGroupScopeService; + private final RoleService roleService; @Override public PageResp page(QuerySaasPermissionGroupReq req) { @@ -199,15 +204,36 @@ public class PermissionGroupImpl implements PermissionGroupService { @Transactional(rollbackFor = Exception.class) public void savePermissionPoints(SavePermissionGroupPPVO save) { SaasPermissionGroup saasPermissionGroup = getRequiredPermissionGroup(save.getId(), null); - List pgpRelations = Optional.ofNullable(save.getSelectedPPIds()).orElse(new ArrayList<>()).stream().map(ppId -> { - SaasPgroupPermissionRelation target = new SaasPgroupPermissionRelation(); - target.setGroupId(saasPermissionGroup.getId()); - target.setFeatureId(ppId); - target.setCreateBy(save.getOperatorId()); - target.setUpdateBy(save.getOperatorId()); - return target; - }).collect(Collectors.toList()); - saasPgroupPermissionRelationService.saveOrUpdate(Lists.newArrayList(saasPermissionGroup.getId()), pgpRelations); + List pgpRelations = resolvePermissionRelations(save, saasPermissionGroup); + + SaasPgroupPermissionRelationService.UpsertPermissionRelationParam upsertPermissionRelationParam = SaasPgroupPermissionRelationService.UpsertPermissionRelationParam.builder() + .groupIds(Lists.newArrayList(saasPermissionGroup.getId())) + .relations(pgpRelations) + .type(OLD_FEATURE) + .build(); + saasPgroupPermissionRelationService.saveOrUpdate(upsertPermissionRelationParam); + } + + private List resolvePermissionRelations(SavePermissionGroupPPVO param, + SaasPermissionGroup saasPermissionGroup) { + if (CollectionUtils.isEmpty(param.getSelectedPPIds())) { + return Lists.newArrayList(); + } + + Map saasFeatureTypeMap = roleService.validFeature(param.getSelectedPPIds()).stream() + .collect(Collectors.toMap(SaasFeature::getId, SaasFeature::getFeatureType)); + + return param.getSelectedPPIds().stream() + .map(ppId -> { + SaasPgroupPermissionRelation target = new SaasPgroupPermissionRelation(); + target.setGroupId(saasPermissionGroup.getId()); + target.setFeatureId(ppId); + target.setCreateBy(param.getOperatorId()); + target.setUpdateBy(param.getOperatorId()); + target.setFeatureType(saasFeatureTypeMap.get(ppId)); + target.setType(OLD_FEATURE); + return target; + }).collect(Collectors.toList()); } @Override @@ -377,7 +403,7 @@ public class PermissionGroupImpl implements PermissionGroupService { throw new ServiceException("选择类型设置错误"); } if (CollectionUtils.containsAny(includeScopes, excludeScopes)) { - throw new ServiceException("项目部例外设置冲突"); + throw new ServiceException("项目例外设置冲突"); } scopeTypes.addAll(selectedWorkspace.stream().map(SaveOrUpdatePermissionGroupVO.PermissionGroupScopeVO::getType).collect(Collectors.toSet())); } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionPointServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionPointServiceImpl.java index f51c9716..774539f0 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionPointServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionPointServiceImpl.java @@ -1,49 +1,65 @@ package cn.axzo.tyr.server.service.impl; -import static cn.axzo.tyr.client.model.enums.FeatureType.BUTTON; -import static cn.axzo.tyr.client.model.enums.FeatureType.MODULE; -import static cn.axzo.tyr.server.common.constants.PermissionConstant.*; - -import java.util.*; -import java.util.function.Function; -import java.util.stream.Collectors; - -import cn.axzo.tyr.client.model.enums.FeatureDataType; -import cn.axzo.tyr.client.model.req.QueryPermissionByIdsReq; -import cn.axzo.tyr.client.model.res.SimplePermissionPointResp; -import cn.hutool.core.date.StopWatch; -import cn.hutool.core.lang.Opt; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import com.baomidou.mybatisplus.core.conditions.Wrapper; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; - import cn.axzo.basics.common.BeanMapper; import cn.axzo.basics.common.util.StopWatchUtil; import cn.axzo.basics.common.util.TreeUtil; import cn.axzo.framework.domain.web.code.BaseCode; +import cn.axzo.tyr.client.common.enums.PermissionRelationOperateLogSceneEnum; import cn.axzo.tyr.client.model.dict.request.BasicDictNodeReq; import cn.axzo.tyr.client.model.dict.request.BasicDictQueryReq; import cn.axzo.tyr.client.model.dict.response.BasicDictNodeResp; import cn.axzo.tyr.client.model.dict.response.BasicDictTreeResp; import cn.axzo.tyr.client.model.enums.DictTypeFiledEnum; import cn.axzo.tyr.client.model.enums.DictWorkSpaceTypeEnum; +import cn.axzo.tyr.client.model.enums.FeatureDataType; import cn.axzo.tyr.client.model.enums.FeatureType; -import cn.axzo.tyr.client.model.permission.*; +import cn.axzo.tyr.client.model.permission.DeletePermissionPointRequest; +import cn.axzo.tyr.client.model.permission.PermissionPointDTO; +import cn.axzo.tyr.client.model.permission.PermissionPointListQueryRequest; +import cn.axzo.tyr.client.model.permission.PermissionPointMoveRequest; +import cn.axzo.tyr.client.model.permission.PermissionPointTreeNode; +import cn.axzo.tyr.client.model.permission.PermissionPointTreeQueryReq; +import cn.axzo.tyr.client.model.permission.PermissionPointVO; +import cn.axzo.tyr.client.model.req.PermissionOperateLogReq; +import cn.axzo.tyr.client.model.req.QueryPermissionByIdsReq; +import cn.axzo.tyr.client.model.res.SimplePermissionPointResp; import cn.axzo.tyr.server.common.util.Throws; import cn.axzo.tyr.server.repository.dao.SaasFeatureDao; import cn.axzo.tyr.server.repository.dao.SaasPgroupPermissionRelationDao; import cn.axzo.tyr.server.repository.dao.SaasProductModuleFeatureRelationDao; import cn.axzo.tyr.server.repository.entity.SaasFeature; +import cn.axzo.tyr.server.repository.mapper.SaasFeatureMapper; import cn.axzo.tyr.server.service.PermissionPointService; import cn.axzo.tyr.server.service.SaasBasicDictService; +import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationOperateLogService; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionTemplate; -import static cn.axzo.tyr.server.util.RpcInternalUtil.checkAndGetData; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static cn.axzo.tyr.client.model.enums.FeatureType.BUTTON; +import static cn.axzo.tyr.client.model.enums.FeatureType.MODULE; +import static cn.axzo.tyr.server.common.constants.PermissionConstant.FEATURE_BIZ_NO_PREFIX; +import static cn.axzo.tyr.server.common.constants.PermissionConstant.FEATURE_PATH_DELIMITER; +import static cn.axzo.tyr.server.common.constants.PermissionConstant.FEATURE_TOP_BIZ_NO; +import static cn.axzo.tyr.server.common.constants.PermissionConstant.FEATURE_TOP_PATH; /** * 权限点服务实现 @@ -57,10 +73,13 @@ import static cn.axzo.tyr.server.util.RpcInternalUtil.checkAndGetData; @Service public class PermissionPointServiceImpl implements PermissionPointService { + private final SaasFeatureMapper saasFeatureMapper; private final SaasFeatureDao saasFeatureDao; private final SaasBasicDictService saasBasicDictService; private final SaasPgroupPermissionRelationDao saasPgroupPermissionRelationDao; private final SaasProductModuleFeatureRelationDao saasProductModuleFeatureRelationDao; + private final SaasPgroupPermissionRelationOperateLogService saasPgroupPermissionRelationOperateLogService; + private final TransactionTemplate transactionTemplate; @Override public List listTreeNodes(PermissionPointTreeQueryReq request) { @@ -334,6 +353,11 @@ public class PermissionPointServiceImpl implements PermissionPointService { this.saasFeatureDao.updateById(saasFeature); //返回一些要用的数据 dto.setBusinessNo(feature.getBusinessNo()); + try { + saveOperateLogForInsertOrUpdate(dto, feature.getId()); + } catch (Exception e) { + log.warn("error save operate log", e); + } return dto; } @@ -368,9 +392,26 @@ public class PermissionPointServiceImpl implements PermissionPointService { dto.setBusinessNo(saasFeature.getBusinessNo()); //调整排序 - 兼容处理老数据,数据规范化 changeSort(saasFeature, saasFeature.getSort()); + try { + saveOperateLogForInsertOrUpdate(dto, saasFeature.getId()); + } catch (Exception e) { + log.warn("error save operate log", e); + } return dto; } + private void saveOperateLogForInsertOrUpdate(PermissionPointDTO request, Long permissionId) { + SaasFeature feature = saasFeatureDao.getById(permissionId); + PermissionOperateLogReq log = new PermissionOperateLogReq(); + log.setTableName(SaasPgroupPermissionRelationOperateLogServiceImpl.TABLE_NAME_SAAS_FEATURE); + log.setScene(PermissionRelationOperateLogSceneEnum.OLD_OMS_SAAS_FEATURE__SAVE_OR_UPDATE.getValue()); + log.setSceneId(permissionId + ""); + log.setOperatorId(request.determineOperator().getPersonId()); + log.setRequestData(request); + log.setOperateData(feature); + saasPgroupPermissionRelationOperateLogService.save(log); + } + private void checkParentType(FeatureType featureType, SaasFeature parent) { //检查父级元素类型 switch (featureType) { @@ -399,9 +440,21 @@ public class PermissionPointServiceImpl implements PermissionPointService { } } - @Transactional(rollbackFor = Throwable.class) @Override - public List delete(Long permissionPointId) { + @SuppressWarnings({ + "SpringTransactionalMethodCallsInspection", + "It's good, we use transaction template" + }) + public List delete(Long permissionId) { + DeletePermissionPointRequest request = new DeletePermissionPointRequest(); + request.setPermissionId(permissionId); + return transactionTemplate.execute(unused -> deleteV2(request)); + } + + @Override + @Transactional(rollbackFor = Throwable.class) + public List deleteV2(DeletePermissionPointRequest request) { + Long permissionPointId = request.getPermissionId(); List delIds = new ArrayList<>(); List bizNoList = new ArrayList<>(); SaasFeature feature = getAndCheck(permissionPointId); @@ -427,9 +480,26 @@ public class PermissionPointServiceImpl implements PermissionPointService { //删除关联数据 this.saasPgroupPermissionRelationDao.removeByPermissionPointIds(delIds); this.saasProductModuleFeatureRelationDao.removeByPermissionPointIds(delIds); + try { + // 记这条主记录的日志就够了 + saveOperateLogForDelete(request); + } catch (Exception e) { + log.warn("error save operate log", e); + } return bizNoList; } + private void saveOperateLogForDelete(DeletePermissionPointRequest request) { + SaasFeature deletedFeature = saasFeatureMapper.getByIdDeleteAware(request.getPermissionId()); + PermissionOperateLogReq log = new PermissionOperateLogReq(); + log.setTableName(SaasPgroupPermissionRelationOperateLogServiceImpl.TABLE_NAME_SAAS_FEATURE); + log.setScene(PermissionRelationOperateLogSceneEnum.OLD_OMS_SAAS_FEATURE__DELETE.getValue()); + log.setSceneId(request.getPermissionId() + ""); + log.setOperatorId(request.determineOperator().getPersonId()); + log.setRequestData(request); + log.setOperateData(deletedFeature); + saasPgroupPermissionRelationOperateLogService.save(log); + } @Override @Transactional(rollbackFor = Exception.class) diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionQueryServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionQueryServiceImpl.java index 3e8c2c32..43a267bd 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionQueryServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/PermissionQueryServiceImpl.java @@ -5,19 +5,38 @@ import cn.axzo.basics.common.util.AssertUtil; import cn.axzo.basics.common.util.NumberUtil; import cn.axzo.basics.common.util.TreeUtil; import cn.axzo.framework.auth.domain.TerminalInfo; +import cn.axzo.framework.domain.web.result.ApiResult; import cn.axzo.maokai.common.enums.SaasCooperateShipCooperateTypeEnum; +import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import cn.axzo.pokonyan.util.TraceSupplier; +import cn.axzo.thrones.client.saas.ServicePkgClient; +import cn.axzo.thrones.client.saas.entity.serivicepgkproduct.ServicePkgProduct; +import cn.axzo.thrones.client.saas.entity.servicepkg.ServicePkgDetailRes; import cn.axzo.tyr.client.common.enums.FeatureResourceAuthType; import cn.axzo.tyr.client.common.enums.FeatureResourceType; import cn.axzo.tyr.client.common.enums.RoleTypeEnum; +import cn.axzo.tyr.client.model.base.FeatureResourceExtraDO; import cn.axzo.tyr.client.model.base.WorkspaceOUPair; import cn.axzo.tyr.client.model.enums.IdentityType; +import cn.axzo.tyr.client.model.product.ProductFeatureRelationVO; +import cn.axzo.tyr.client.model.req.FeatureIdPair; import cn.axzo.tyr.client.model.req.IdentityAuthReq; import cn.axzo.tyr.client.model.req.NavTreeReq; import cn.axzo.tyr.client.model.req.PagePermissionReq; import cn.axzo.tyr.client.model.req.PagePermissionResp; +import cn.axzo.tyr.client.model.req.PageSaasFeatureResourceReq; import cn.axzo.tyr.client.model.req.PermissionCheckReq; +import cn.axzo.tyr.client.model.req.TreePermissionReq; +import cn.axzo.tyr.client.model.req.TreeProductFeatureResourceReq; +import cn.axzo.tyr.client.model.res.FeatureResourceDTO; import cn.axzo.tyr.client.model.res.IdentityAuthRes; import cn.axzo.tyr.client.model.res.NavTreeResp; +import cn.axzo.tyr.client.model.res.ProductFeatureResourceResp; +import cn.axzo.tyr.client.model.res.SaasFeatureResourceResp; +import cn.axzo.tyr.client.model.res.SaasPermissionRelationRes; +import cn.axzo.tyr.client.model.res.TreePermissionResp; +import cn.axzo.tyr.client.model.roleuser.dto.SaasRoleUserV2DTO; +import cn.axzo.tyr.client.model.roleuser.req.ListRoleUserRelationParam; import cn.axzo.tyr.server.model.PermissionDO; import cn.axzo.tyr.server.model.PermissionQueryContext; import cn.axzo.tyr.server.model.ResourcePermission; @@ -25,21 +44,38 @@ import cn.axzo.tyr.server.model.ResourcePermissionQueryDTO; import cn.axzo.tyr.server.model.RoleWithFeature; import cn.axzo.tyr.server.model.UserIdentity; import cn.axzo.tyr.server.model.WorkspaceFeatureRelation; +import cn.axzo.tyr.server.repository.dao.ProductModuleDao; +import cn.axzo.tyr.server.repository.dao.SaasFeatureResourceDao; +import cn.axzo.tyr.server.repository.dao.SaasPageElementFeatureResourceRelationDao; import cn.axzo.tyr.server.repository.entity.SaasFeatureResource; +import cn.axzo.tyr.server.repository.entity.SaasProductModuleFeatureRelation; import cn.axzo.tyr.server.repository.entity.SaasRoleUserRelation; import cn.axzo.tyr.server.service.PermissionQueryService; +import cn.axzo.tyr.server.service.ProductFeatureRelationService; import cn.axzo.tyr.server.service.RoleService; import cn.axzo.tyr.server.service.SaasFeatureResourceService; +import cn.axzo.tyr.server.service.SaasRoleUserRelationService; import cn.axzo.tyr.server.service.TyrSaasAuthService; +import cn.axzo.tyr.server.service.WorkspaceProductService; import cn.axzo.tyr.server.util.KeyUtil; +import cn.axzo.tyr.server.utils.RpcInternalUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.stereotype.Service; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -47,10 +83,16 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; import java.util.function.Function; import java.util.stream.Collectors; +import static cn.axzo.tyr.server.repository.entity.SaasFeatureResource.DISPLAY_STATUS; +import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.NEW_FEATURE; + /** * 权限查询服务实现 * @@ -61,12 +103,32 @@ import java.util.stream.Collectors; @Slf4j @Service @RequiredArgsConstructor +@RefreshScope public class PermissionQueryServiceImpl implements PermissionQueryService { private final SaasFeatureResourceService featureResourceService; private final RoleUserService roleUserService; private final RoleService roleService; private final TyrSaasAuthService saasAuthService; + private final ServicePkgClient servicePkgClient; + private final ProductModuleDao productModuleDao; + private final ProductFeatureRelationService productFeatureRelationService; + private final SaasFeatureResourceDao saasFeatureResourceDao; + private final SaasFeatureResourceService saasFeatureResourceService; + private final SaasRoleUserRelationService saasRoleUserRelationService; + private final WorkspaceProductService workspaceProductService; + private final SaasPageElementFeatureResourceRelationDao saasPageElementFeatureResourceRelationDao; + + @Qualifier("authExecutor") + @Autowired + private Executor executor; + + + @Value("${use.old.auth:true}") + private boolean USE_OLD_AUTH; + + @Value("${not.auth.uniCodes:}") + private Set notAuthUniCodes; @Override public List getNavTree(NavTreeReq req) { @@ -80,7 +142,6 @@ public class PermissionQueryServiceImpl implements PermissionQueryService { //查询权限 List permissions = queryUserPermission(context); -// List permissions = queryAllPermission(context); if (CollectionUtil.isEmpty(permissions)) { return Collections.emptyList(); } @@ -104,17 +165,45 @@ public class PermissionQueryServiceImpl implements PermissionQueryService { .filter(r -> distinctSet.add(r.getId())) .sorted(Comparator.comparingInt(SaasFeatureResource::getDisplayOrder)) .collect(Collectors.toList()); + + // 因为存在中间某个节点被隐藏,下面节点不需要显示的情况,所以构建后的树只要父节点不是根节点,都过滤掉 + SaasFeatureResource root = saasFeatureResourceDao.lambdaQuery() + .eq(SaasFeatureResource::getTerminal, req.getTerminal()) + .eq(SaasFeatureResource::getParentId, 0L) + .one(); + //组装导航树 List navTreeList = TreeUtil.buildTree(BeanMapper.copyList(finalResources, NavTreeResp.class)); //过滤顶级孤儿节点 - return navTreeList.stream().filter(t -> !NumberUtil.isPositiveNumber(t.getParentId())).collect(Collectors.toList()); + return navTreeList.stream().filter(t -> Objects.equals(t.getParentId(), root.getId())).collect(Collectors.toList()); } @Override public boolean hasPermission(PermissionCheckReq req) { + //这里暂时硬编码-非OMS端鉴权请求 直接转老接口处理 - if (!StrUtil.equals("NT_OMS_WEB" ,req.getTerminal())) { - return hasPermissionV2(req); + if (!StrUtil.equals("NT_OMS_WEB" ,req.getTerminal()) + && !Objects.equals(TerminalInfo.NT_PC_GA_GENERAL, req.getTerminal())) { + if (USE_OLD_AUTH) { + log.info("user old auth"); + return hasPermissionV2(req); + } + log.info("user new auth"); + + // 为了兼容用户没有在企业中,但是需要看到企业类型的菜单,在调用相关接口时,忽略这些菜单的鉴权 + if (CollectionUtils.isNotEmpty(notAuthUniCodes)) { + Sets.SetView intersection = Sets.intersection(notAuthUniCodes, Sets.newHashSet(req.getFeatureCodes())); + if (CollectionUtils.isNotEmpty(intersection)) { + return true; + } + } + + List> authFutures = Lists.newArrayList(); + authFutures.add(CompletableFuture.supplyAsync(TraceSupplier.create(() -> saasAuthService.authPermission(req)), executor)); + authFutures.add(CompletableFuture.supplyAsync(TraceSupplier.create(() -> saasAuthService.authNewPermission(req)), executor)); + return authFutures.stream() + .anyMatch(CompletableFuture::join); + } //权限编码转ID List resourcePermissions = featureResourceService.permissionQuery( @@ -167,7 +256,6 @@ public class PermissionQueryServiceImpl implements PermissionQueryService { .appendPersonId(req.getPersonId()); //查询权限 List permissions = queryUserPermission(context); -// List permissions = queryAllPermission(context); Set featureIds = permissions.stream().map(PermissionDO::getFeatureIds).flatMap(Set::stream).collect(Collectors.toSet()); //权限过滤 return resourceList.stream() @@ -178,19 +266,206 @@ public class PermissionQueryServiceImpl implements PermissionQueryService { .collect(Collectors.toList()); } - private List queryAllPermission(PermissionQueryContext context) { - //mock 调试用 返回全部权限 - List workspaceFeatureRelations = listWorkspaceFeatureRelations(context); - if (CollectionUtil.isEmpty(workspaceFeatureRelations)) { + @Override + public List treeProduct(TreeProductFeatureResourceReq request) { + List saasFeatureResources = listFeatureResource(request); + + if (CollectionUtil.isEmpty(saasFeatureResources)) { return Collections.emptyList(); } - Set featureIds = workspaceFeatureRelations.stream() - .map(WorkspaceFeatureRelation::getFeatureId) - .collect(Collectors.toSet()); - PermissionDO permission = PermissionDO.builder().featureIds(featureIds).build(); - return Collections.singletonList(permission); + + List collect = saasFeatureResources.stream() + .map(this::resolveProductFeatureResourceResp) + .sorted(Comparator.comparing(ProductFeatureResourceResp::getDisplayOrder)) + .collect(Collectors.toList()); + + return TreeUtil.buildTree(collect); } + @Override + public List listFeatureResource(TreeProductFeatureResourceReq request) { + List productFeatureRelations = getProductFeatureRelationByWorkspace(Sets.newHashSet(request.getWorkspaceId())); + if (CollectionUtil.isEmpty(productFeatureRelations)) { + return Collections.emptyList(); + } + + List featureIds = productFeatureRelations.stream() + .map(ProductFeatureRelationVO::getFeatureId) + .distinct() + .collect(Collectors.toList()); + + return featureResourceService.listByParentIdAndTerminalAndIds(null, request.getTerminal(), featureIds).stream() + .map(e -> { + FeatureResourceDTO featureResourceDTO = FeatureResourceDTO.builder().build(); + BeanUtils.copyProperties(e, featureResourceDTO); + return featureResourceDTO; + }) + .collect(Collectors.toList()); + } + + /** + * 可能会出现用户在项目不在企业的历史数据,但是又需要看到企业的某些菜单(无法配置权限), + * 所以做了一个开关可以配置所有用户可以看到的菜单 + * @return + */ + private Set listNotAuthFeatureIds() { + if (CollectionUtils.isEmpty(notAuthUniCodes)) { + return Collections.emptySet(); + } + + PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder() + .uniCodes(notAuthUniCodes) + .build(); + return featureResourceService.list(pageSaasFeatureResourceReq).stream() + .map(SaasFeatureResourceResp::getId) + .collect(Collectors.toSet()); + + } + + @Override + public List treePermission(TreePermissionReq req) { + + Set allFeatureIds = Sets.newHashSet(); + Set featureIds = listUserPermissionFeatureIds(req); + + Set defaultFeatureIds = listNotAuthFeatureIds(); + allFeatureIds.addAll(featureIds); + allFeatureIds.addAll(defaultFeatureIds); + if (CollectionUtils.isEmpty(allFeatureIds)) { + return Collections.emptyList(); + } + + List saasFeatureResources = saasFeatureResourceService.list(PageSaasFeatureResourceReq.builder() + .ids(Lists.newArrayList(allFeatureIds)) + .needFeatureCodes(true) + .terminal(req.getTerminal()) + .build()); + // 有可能存在资源树被删除的情况 + // 因为会存在用户只有菜单权限,下面没有节点,这种需要过滤掉 + if (StringUtils.isBlank(req.getUniCode())) { + saasFeatureResources = filterFeature(saasFeatureResources); + } + if (CollectionUtils.isEmpty(saasFeatureResources)) { + return Collections.emptyList(); + } + + List treePermissionResps = saasFeatureResources.stream() + .map(e -> { + TreePermissionResp treePermissionResp = TreePermissionResp.builder().build(); + BeanUtils.copyProperties(e, treePermissionResp); + treePermissionResp.setFeatureId(e.getId()); + FeatureResourceExtraDO extra = e.getExtra(); + if (Objects.nonNull(extra)) { + treePermissionResp.setActiveIcon(extra.getActiveIcon()); + treePermissionResp.setMoreIcon(extra.getMoreIcon()); + } + return treePermissionResp; + }) + .sorted(Comparator.comparing(TreePermissionResp::getDisplayOrder)) + .collect(Collectors.toList()); + // 组装导航树 + // 过滤掉隐藏的节点,因为存在某些节点被隐藏,需要把这些节点和子节点给过滤掉 + List result = TreeUtil.buildTree(treePermissionResps, + (Function) e -> Objects.equals(DISPLAY_STATUS, e.getStatus())); + // 分割线的配置是临时方案,二级菜单前面全是分割线的需要过滤掉,所以做特殊处理过滤掉二级菜单的前面全是分割线的数据 + result.forEach(e -> { + List children = e.getChildren(); + if (CollectionUtils.isEmpty(children)) { + return; + } + + for (int i = 0; i < children.size();) { + if (!Objects.equals(children.get(i).getFeatureType(), FeatureResourceType.MENU_PARTITION_GROUP.getCode())) { + return; + } + children.remove(i); + } + }); + return result; + } + + private List filterFeature(List saasFeatureResources) { + if (CollectionUtils.isEmpty(saasFeatureResources)) { + return Collections.emptyList(); + } + + List excludeMenuFeatures = saasFeatureResources.stream() + .filter(e -> Objects.equals(DISPLAY_STATUS, e.getStatus())) + .filter(e -> !Objects.equals(FeatureResourceType.MENU.getCode(), e.getFeatureType())) + .collect(Collectors.toList()); + + if (CollectionUtils.isEmpty(excludeMenuFeatures)) { + return Collections.emptyList(); + } + // 根据页面找父级所有菜单节点 + Set excludeMenuFeatureIds = excludeMenuFeatures.stream() + .map(e -> Lists.newArrayList(e.getPath().split(","))) + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .map(Long::valueOf) + .collect(Collectors.toSet()); + + + List menuFeatures = saasFeatureResources.stream() + .filter(e -> excludeMenuFeatureIds.contains(e.getId())) + .filter(e -> Objects.equals(FeatureResourceType.MENU.getCode(), e.getFeatureType())) + .collect(Collectors.toList()); + + excludeMenuFeatures.addAll(menuFeatures); + return excludeMenuFeatures; + } + + private List getProductFeatureRelationByWorkspace(Set workspaceIds) { + List servicePkgDetailRes = RpcInternalUtil.rpcListProcessor(() -> servicePkgClient.getServicePkgDetailBySpaceId(workspaceIds), + "查询租户的产品", workspaceIds).getData(); + if (CollectionUtil.isEmpty(servicePkgDetailRes)) { + return Collections.emptyList(); + } + + List productIds = servicePkgDetailRes.stream() + .map(ServicePkgDetailRes::getProducts) + .filter(CollectionUtil::isNotEmpty) + .flatMap(List::stream) + .map(ServicePkgProduct::getProductId) + .distinct() + .collect(Collectors.toList()); + if (CollectionUtil.isEmpty(productIds)) { + log.warn("no product found for workspace :{}", workspaceIds); + return Collections.emptyList(); + } + + // 已被删除产品过滤一层 + productIds = productModuleDao.listByIds(productIds) + .stream() + .filter(productModule -> Objects.equals(productModule.getIsDelete(),0L)) + .map(BaseEntity::getId) + .distinct() + .collect(Collectors.toList()); + if (CollectionUtil.isEmpty(productIds)) { + log.warn("all product is deleted for workspace :{}", workspaceIds); + return Collections.emptyList(); + } + + ApiResult> listProductFeatureRelation = productFeatureRelationService.featureListByProduct(productIds); + if (listProductFeatureRelation.isError() || CollectionUtil.isEmpty(listProductFeatureRelation.getData())) { + log.warn("no product features found for productIds :{} result:{}", productIds, listProductFeatureRelation); + return Collections.emptyList(); + } + + return listProductFeatureRelation.getData(); + } + + private ProductFeatureResourceResp resolveProductFeatureResourceResp(FeatureResourceDTO featureResource) { + return ProductFeatureResourceResp.builder() + .id(featureResource.getId()) + .featureCode(featureResource.getFeatureCode()) + .featureName(featureResource.getFeatureName()) + .featureType(featureResource.getFeatureType()) + .terminal(featureResource.getTerminal()) + .parentId(featureResource.getParentId()) + .displayOrder(featureResource.getDisplayOrder()) + .build(); + } private List queryUserPermission(PermissionQueryContext context) { //查询用户具有的角色 @@ -278,19 +553,49 @@ public class PermissionQueryServiceImpl implements PermissionQueryService { private List listWorkspaceFeatureRelations(PermissionQueryContext context) { //TODO:@Zhan 本期没做产品权限配置,这里暂时先查询所有OMS资源作为已配置产品权限 - List permissions = featureResourceService.permissionQuery(ResourcePermissionQueryDTO.builder() - .terminals(Collections.singletonList(TerminalInfo.NT_OMS_WEB)) - .build()); + // 接口调用方用户传入端则以它为准,没传入,则兼容历史情况,查询NT_OMS_WEB + String terminal = StringUtils.isBlank(context.getTerminal()) ? TerminalInfo.NT_OMS_WEB : context.getTerminal(); List result = new ArrayList<>(); - for (WorkspaceOUPair ow : context.getWorkspaceOUPairs()) { - List owPermission = permissions.stream() - .map(p -> WorkspaceFeatureRelation.builder() - .workspaceId(ow.getWorkspaceId()) - .productUnitType(SaasCooperateShipCooperateTypeEnum.OMS.code) - .featureId(p.getId()) - .build()) - .collect(Collectors.toList()); - result.addAll(owPermission); + if (TerminalInfo.NT_OMS_WEB.equals(terminal)) { + List permissions = featureResourceService.permissionQuery(ResourcePermissionQueryDTO.builder() + .terminals(Collections.singletonList(terminal)) + .build()); + for (WorkspaceOUPair ow : context.getWorkspaceOUPairs()) { + List owPermission = permissions.stream() + .map(p -> WorkspaceFeatureRelation.builder() + .workspaceId(ow.getWorkspaceId()) + .productUnitType(SaasCooperateShipCooperateTypeEnum.OMS.code) + .featureId(p.getId()) + .build()) + .collect(Collectors.toList()); + result.addAll(owPermission); + } + } else { + // 其它端则查询租户的产品权限 + Set workspaceIds = context.getWorkspaceOUPairs().stream().map(WorkspaceOUPair::getWorkspaceId).collect(Collectors.toSet()); + List productFeatureRelations = getProductFeatureRelationByWorkspace(workspaceIds); + if (CollectionUtil.isEmpty(productFeatureRelations)) { + return result; + } + for (WorkspaceOUPair ow : context.getWorkspaceOUPairs()) { + List owPermission = productFeatureRelations.stream() + .map(p -> { + WorkspaceFeatureRelation relation = WorkspaceFeatureRelation.builder() + .workspaceId(ow.getWorkspaceId()) + .featureId(p.getFeatureId()) + .productUnitType(0) + .build(); + if (StringUtils.isNotBlank(p.getDictCode())) { + try { + relation.setProductUnitType(Integer.parseInt(p.getDictCode())); + } catch(Exception e){ + log.warn("productDictCode to ProductUnitType error."); + } + } + return relation; + }).collect(Collectors.toList()); + result.addAll(owPermission); + } } return result; } @@ -345,4 +650,155 @@ public class PermissionQueryServiceImpl implements PermissionQueryService { } return relations; } + + private Set listUserPermissionFeatureIds(TreePermissionReq treePermissionReq) { + + List featureIds = resolveFeatureIds(treePermissionReq); + + if (StringUtils.isNotBlank(treePermissionReq.getUniCode()) && CollectionUtils.isEmpty(featureIds)) { + return Collections.emptySet(); + } + + List saasRoleUserV2DTOS = listUserPermission(treePermissionReq, featureIds); + + // 用户可能没有角色 + if (CollectionUtils.isEmpty(saasRoleUserV2DTOS)) { + return Collections.emptySet(); + } + + List workspaceProducts = listWorkspaceProducts(treePermissionReq, featureIds); + + //免授权 + List authFreeFeatureIds = listNotAuthFeatures(treePermissionReq); + + //取交集确定权限 + return mixFeatureIds(saasRoleUserV2DTOS, workspaceProducts, authFreeFeatureIds); + } + + private List listNotAuthFeatures(TreePermissionReq treePermissionReq) { + PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder() + .terminal(treePermissionReq.getTerminal()) + .authType(FeatureResourceAuthType.ALL_ROLE.getCode()) + .build(); + return featureResourceService.list(pageSaasFeatureResourceReq).stream() + .map(SaasFeatureResourceResp::getId) + .collect(Collectors.toList()); + } + + private List listWorkspaceProducts(TreePermissionReq treePermissionReq, + List featureIds) { + //查询租户产品权限点 + Set workspaceIds = treePermissionReq.getWorkspaceOUPairs().stream() + .map(WorkspaceOUPair::getWorkspaceId) + .collect(Collectors.toSet()); + + WorkspaceProductService.WorkspaceProductParam workspaceProductParam = WorkspaceProductService.WorkspaceProductParam.builder() + .terminal(treePermissionReq.getTerminal()) + .workspaceIds(workspaceIds) + .featureResourceTypes(treePermissionReq.getFeatureResourceTypes()) + .type(NEW_FEATURE) + .build(); + + if (CollectionUtils.isNotEmpty(featureIds)) { + workspaceProductParam.setFeatureIdPairs(Lists.newArrayList(FeatureIdPair.builder() + .featureIds(Sets.newHashSet(featureIds)) + .type(NEW_FEATURE) + .build())); + } + return workspaceProductService.listWorkspaceProduct(workspaceProductParam); + } + + private List listUserPermission(TreePermissionReq treePermissionReq, List featureIds) { + List workspaceOuPairs = treePermissionReq.getWorkspaceOUPairs().stream() + .map(e -> ListRoleUserRelationParam.WorkspaceOuPair.builder() + .workspaceId(e.getWorkspaceId()) + .ouId(e.getOuId()) + .build()) + .collect(Collectors.toList()); + + ListRoleUserRelationParam listRoleUserRelationParam = ListRoleUserRelationParam.builder() + .personId(treePermissionReq.getPersonId()) + .workspaceOuPairs(Lists.newArrayList(workspaceOuPairs)) + .needRole(true) + .needPermissionRelation(true) + .featureResourceTypes(treePermissionReq.getFeatureResourceTypes()) + .type(NEW_FEATURE) + .terminal(treePermissionReq.getTerminal()) + .featureIds(featureIds) + .build(); + return saasRoleUserRelationService.listV2(listRoleUserRelationParam).stream() + .filter(e -> e.getSaasRole() != null) + .collect(Collectors.toList()); + } + + private List resolveFeatureIds(TreePermissionReq treePermissionReq) { + if (StringUtils.isBlank(treePermissionReq.getUniCode())) { + return Collections.emptyList(); + } + + PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder() + .uniCodes(Sets.newHashSet(treePermissionReq.getUniCode())) + .build(); + return featureResourceService.list(pageSaasFeatureResourceReq).stream() + .map(SaasFeatureResourceResp::getId) + .collect(Collectors.toList()); + } + + private Set mixFeatureIds(List saasRoleUsers, + List workspaceProducts, + List authFreeFeatureIds) { + + Map workspaceProductMap = workspaceProducts.stream() + .collect(Collectors.toMap(WorkspaceProductService.WorkspaceProduct::getWorkspaceId, Function.identity())); + + return saasRoleUsers.stream() + .filter(roleUser -> { + WorkspaceProductService.WorkspaceProduct workspaceProduct = workspaceProductMap.get(roleUser.getSaasRoleUser().getWorkspaceId()); + if (workspaceProduct == null || CollectionUtils.isEmpty(workspaceProduct.getSaasProductModuleFeatureRelations())) { + log.warn("no workspace product feature found for id:{}", roleUser.getSaasRoleUser().getWorkspaceId()); + return false; + } + return true; + }) + .map(roleUser -> { + WorkspaceProductService.WorkspaceProduct workspaceProduct = workspaceProductMap.get(roleUser.getSaasRoleUser().getWorkspaceId()); + + SaasRoleUserV2DTO.SaasRole saasRole = roleUser.getSaasRole(); + if (RoleTypeEnum.isAdmin(saasRole.getRoleType())) { + return resolveAdminRole(workspaceProduct, saasRole); + } + return resolveNormalRole(workspaceProduct, saasRole, authFreeFeatureIds); + }) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + } + + private List resolveAdminRole(WorkspaceProductService.WorkspaceProduct workspaceProduct, + SaasRoleUserV2DTO.SaasRole saasRole) { + + //超管和管理员 直接取和角色类型匹配的租户产品权限 + return workspaceProduct.getSaasProductModuleFeatureRelations().stream() + .filter(f -> Objects.equals(f.getDictCode(), saasRole.getProductUnitType().toString()) + || !NumberUtil.isPositiveNumber(saasRole.getProductUnitType())) + .map(SaasProductModuleFeatureRelation::getFeatureId) + .collect(Collectors.toList()); + } + + private List resolveNormalRole(WorkspaceProductService.WorkspaceProduct workspaceProduct, + SaasRoleUserV2DTO.SaasRole saasRole, + List authFreeFeatureIds) { + //普通角色:角色同类型的租户产品权限已分配 且角色上已分配 + 免授权 + Set roleFeatureIds = Optional.ofNullable(saasRole.getPermissionRelations()) + .map(e -> e.stream() + .map(SaasPermissionRelationRes::getFeatureId) + .collect(Collectors.toSet())) + .orElseGet(Collections::emptySet); + + return workspaceProduct.getSaasProductModuleFeatureRelations().stream() + .filter(f -> Objects.equals(f.getDictCode(), saasRole.getProductUnitType().toString()) + || !NumberUtil.isPositiveNumber(saasRole.getProductUnitType())) + .map(SaasProductModuleFeatureRelation::getFeatureId) + .filter(id -> roleFeatureIds.contains(id) || authFreeFeatureIds.contains(id)) + .collect(Collectors.toList()); + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductFeatureRelationServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductFeatureRelationServiceImpl.java index 4909d1cf..846867e3 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductFeatureRelationServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductFeatureRelationServiceImpl.java @@ -1,30 +1,42 @@ package cn.axzo.tyr.server.service.impl; import cn.axzo.basics.common.BeanMapper; -import cn.axzo.basics.common.util.AssertUtil; +import cn.axzo.foundation.dao.support.converter.PageConverter; +import cn.axzo.foundation.dao.support.mysql.QueryWrapperHelper; +import cn.axzo.foundation.page.PageResp; import cn.axzo.framework.domain.web.result.ApiResult; import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; import cn.axzo.thrones.client.saas.ServicePkgClient; import cn.axzo.thrones.client.saas.entity.serivicepgkproduct.ServicePkgProduct; import cn.axzo.thrones.client.saas.entity.servicepkg.ServicePkgDetailRes; -import cn.axzo.tyr.client.model.permission.PermissionPointTreeNode; +import cn.axzo.tyr.client.common.enums.FeatureResourceType; +import cn.axzo.tyr.client.common.enums.PermissionRelationOperateLogSceneEnum; +import cn.axzo.tyr.client.model.product.OldUpdateFeatureRelationRequestV2; import cn.axzo.tyr.client.model.product.ProductFeatureRelationSearchReq; import cn.axzo.tyr.client.model.product.ProductFeatureRelationUpdateReq; import cn.axzo.tyr.client.model.product.ProductFeatureRelationVO; +import cn.axzo.tyr.client.model.req.FeatureIdPair; +import cn.axzo.tyr.client.model.req.PageProductFeatureRelationReq; +import cn.axzo.tyr.client.model.req.PermissionOperateLogReq; import cn.axzo.tyr.server.repository.dao.ProductModuleDao; import cn.axzo.tyr.server.repository.dao.SaasFeatureDao; +import cn.axzo.tyr.server.repository.dao.SaasFeatureResourceDao; +import cn.axzo.tyr.server.repository.dao.SaasProductModuleFeatureRelationDao; import cn.axzo.tyr.server.repository.entity.ProductFeatureQuery; import cn.axzo.tyr.server.repository.entity.SaasFeature; +import cn.axzo.tyr.server.repository.entity.SaasFeatureResource; import cn.axzo.tyr.server.repository.entity.SaasProductModuleFeatureRelation; -import cn.axzo.tyr.server.repository.dao.SaasProductModuleFeatureRelationDao; +import cn.axzo.tyr.server.repository.mapper.SaasProductModuleFeatureRelationMapper; import cn.axzo.tyr.server.service.ProductFeatureRelationService; +import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationOperateLogService; import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.collection.ListUtil; import cn.hutool.core.date.StopWatch; -import cn.hutool.core.stream.SimpleCollector; import cn.hutool.core.util.StrUtil; -import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.common.collect.Lists; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -33,22 +45,17 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; -import java.util.function.BiConsumer; -import java.util.function.BinaryOperator; import java.util.function.Function; -import java.util.function.Supplier; import java.util.stream.Collectors; import static cn.axzo.tyr.server.util.RpcInternalUtil.checkAndGetData; +import static java.util.stream.Collectors.joining; /** * 产品与权限点的 Service 接口实现 @@ -59,11 +66,14 @@ import static cn.axzo.tyr.server.util.RpcInternalUtil.checkAndGetData; @Slf4j @RequiredArgsConstructor @Service -public class ProductFeatureRelationServiceImpl implements ProductFeatureRelationService { +public class ProductFeatureRelationServiceImpl extends ServiceImpl + implements ProductFeatureRelationService { private final SaasProductModuleFeatureRelationDao saasProductModuleFeatureRelationDao; private final ServicePkgClient servicePkgClient; private final SaasFeatureDao saasFeatureDao; - private final ProductModuleDao productModuleDao; + private final ProductModuleDao productModuleDao; + private final SaasFeatureResourceDao saasFeatureResourceDao; + private final SaasPgroupPermissionRelationOperateLogService saasPgroupPermissionRelationOperateLogService; @Override public ApiResult> featureList(ProductFeatureRelationSearchReq req) { @@ -71,6 +81,7 @@ public class ProductFeatureRelationServiceImpl implements ProductFeatureRelation .eq(SaasProductModuleFeatureRelation::getProductModuleId, req.getProductModuleId()) .eq(Objects.nonNull(req.getDictCodeId()), SaasProductModuleFeatureRelation::getDictCodeId, req.getDictCodeId()) .eq(StringUtils.hasLength(req.getDictCode()), SaasProductModuleFeatureRelation::getDictCode, req.getDictCode()) + .eq(Objects.nonNull(req.getType()), SaasProductModuleFeatureRelation::getType, req.getType()) .list(); return ApiResult.ok(BeanMapper.copyList(list, ProductFeatureRelationVO.class)); } @@ -78,10 +89,23 @@ public class ProductFeatureRelationServiceImpl implements ProductFeatureRelation @Override @Transactional(rollbackFor = Exception.class) public ApiResult updateFeatureRelation(List req) { + OldUpdateFeatureRelationRequestV2 requestV2 = new OldUpdateFeatureRelationRequestV2(); + requestV2.setRelations(req); + requestV2.setOperator(null); + return updateFeatureRelationV2(requestV2); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ApiResult updateFeatureRelationV2(OldUpdateFeatureRelationRequestV2 request) { + if (CollectionUtils.isEmpty(request.getRelations())) { + return ApiResult.ok(false); + } + List req = request.getRelations(); List productIds = req.stream().map(ProductFeatureRelationUpdateReq::getProductModuleId).distinct().collect(Collectors.toList()); if (!CollectionUtils.isEmpty(productIds)) { saasProductModuleFeatureRelationDao.remove(new LambdaQueryWrapper() - .in(SaasProductModuleFeatureRelation::getProductModuleId, productIds)); + .in(SaasProductModuleFeatureRelation::getProductModuleId, productIds).eq(SaasProductModuleFeatureRelation::getType, 0)); } List saveList = new ArrayList<>(); req.forEach(i -> i.getFeatureIds().forEach(featureId -> { @@ -90,29 +114,101 @@ public class ProductFeatureRelationServiceImpl implements ProductFeatureRelation relation.setDictCodeId(i.getDictCodeId()); relation.setDictCode(i.getDictCode()); relation.setFeatureId(featureId); + relation.setType(Objects.isNull(i.getFeatureRelationType()) ? 0 : i.getFeatureRelationType()); saveList.add(relation); })); saasProductModuleFeatureRelationDao.saveBatch(saveList); + + // !! save logs + try { + saveOperateLogForUpdateFeatureRelationV2(request, saveList); + } catch (Exception e) { + log.warn("error save operate log", e); + } + return ApiResult.ok(true); } + private void saveOperateLogForUpdateFeatureRelationV2( + OldUpdateFeatureRelationRequestV2 request, + List saveList) { + String productModuleIds = saveList.stream() + .map(BaseEntity::getId) + .distinct() + .map(String::valueOf) + .collect(joining(",")); + productModuleIds = org.apache.commons.lang3.StringUtils.truncate(productModuleIds, 254); + PermissionOperateLogReq log = new PermissionOperateLogReq(); + log.setTableName(SaasPgroupPermissionRelationOperateLogServiceImpl.TABLE_NAME_PRODUCT_MODULE_FEATURE_RELATION); + log.setScene(PermissionRelationOperateLogSceneEnum.OLD_OMS_PRODUCT_PERMISSION_POINT__SAVE_BATCH.getValue()); + log.setSceneId(productModuleIds); + log.setOperatorId(request.determineOperator().getPersonId()); + log.setRequestData(request); + log.setOperateData(saveList); + saasPgroupPermissionRelationOperateLogService.save(log); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateFeatureResourceRelation(List req, int relationType) { + List productIds = req.stream().map(ProductFeatureRelationUpdateReq::getProductModuleId).distinct().collect(Collectors.toList()); + if (!CollectionUtils.isEmpty(productIds)) { + saasProductModuleFeatureRelationDao.remove(new LambdaQueryWrapper() + .in(SaasProductModuleFeatureRelation::getProductModuleId, productIds).eq(SaasProductModuleFeatureRelation::getType, relationType)); + } + + Set allFeatureResourceIds = req.stream().map(ProductFeatureRelationUpdateReq::getFeatureIds) + .filter(CollectionUtil::isNotEmpty).flatMap(List::stream).collect(Collectors.toSet()); + if (CollectionUtil.isEmpty(allFeatureResourceIds)) { + log.warn("product has no featureResourceIds to bind."); + return; + } + Map resourceMap = saasFeatureResourceDao.lambdaQuery() + .in(BaseEntity::getId, allFeatureResourceIds) + .list().stream().collect(Collectors.toMap(BaseEntity::getId, Function.identity(), (v1, v2) -> v1)); + + List saveList = new ArrayList<>(); + req.forEach(i -> i.getFeatureIds().forEach(featureId -> { + SaasFeatureResource saasFeatureResource = resourceMap.get(featureId); + SaasProductModuleFeatureRelation relation = new SaasProductModuleFeatureRelation(); + relation.setProductModuleId(i.getProductModuleId()); + relation.setDictCodeId(i.getDictCodeId()); + relation.setDictCode(i.getDictCode()); + relation.setFeatureId(featureId); + relation.setType(Objects.isNull(i.getFeatureRelationType()) ? 0 : i.getFeatureRelationType()); + if (Objects.nonNull(saasFeatureResource)) { + relation.setFeatureType(saasFeatureResource.getFeatureType()); + relation.setTerminal(saasFeatureResource.getTerminal()); + } + saveList.add(relation); + })); + saasProductModuleFeatureRelationDao.saveBatch(saveList); + } + + @Override + public void removeFeatureResourceRelationByProductIdAndType(Long productId, int relationType) { + saasProductModuleFeatureRelationDao.remove(new LambdaQueryWrapper() + .eq(SaasProductModuleFeatureRelation::getProductModuleId, productId).eq(SaasProductModuleFeatureRelation::getType, relationType)); + } + @Override public ApiResult> featureListByProduct(List productIds) { if (CollectionUtil.isEmpty(productIds)) { return ApiResult.ok(Collections.emptyList()); } List list = saasProductModuleFeatureRelationDao.lambdaQuery() - .select(SaasProductModuleFeatureRelation::getFeatureId - ,SaasProductModuleFeatureRelation::getProductModuleId - ,SaasProductModuleFeatureRelation::getDictCode - ,SaasProductModuleFeatureRelation::getDictCodeId - , BaseEntity::getId) + .select(SaasProductModuleFeatureRelation::getFeatureId, + SaasProductModuleFeatureRelation::getProductModuleId, + SaasProductModuleFeatureRelation::getDictCode, + SaasProductModuleFeatureRelation::getDictCodeId, + BaseEntity::getId, + SaasProductModuleFeatureRelation::getType) .in(SaasProductModuleFeatureRelation::getProductModuleId, productIds) .list(); return ApiResult.ok(BeanMapper.copyList(list, ProductFeatureRelationVO.class)); } - + @Override public Map> getByWorkspace(Set workspaceId) { StopWatch stopWatch = StopWatch.create(" get product by workspace"); @@ -182,7 +278,25 @@ public class ProductFeatureRelationServiceImpl implements ProductFeatureRelation SaasProductModuleFeatureRelation::getProductModuleId, condition.getProductIds()) .eq(Objects.nonNull(condition.getWorkspaceJoinType()), SaasProductModuleFeatureRelation::getDictCode, condition.getWorkspaceJoinType()) - .in(CollectionUtil.isNotEmpty(condition.getFeatureIds()), SaasProductModuleFeatureRelation::getFeatureId, condition.getFeatureIds()); +// .in(CollectionUtil.isNotEmpty(condition.getFeatureIds()), SaasProductModuleFeatureRelation::getFeatureId, condition.getFeatureIds()) + .eq(Objects.nonNull(condition.getType()), SaasProductModuleFeatureRelation::getType, condition.getType()) + .eq(StringUtils.hasLength(condition.getTerminal()), SaasProductModuleFeatureRelation::getTerminal, condition.getTerminal()); + + if (!CollectionUtils.isEmpty(condition.getFeatureResourceTypes())) { + wrapper.in(SaasProductModuleFeatureRelation::getFeatureType, Lists.transform(condition.getFeatureResourceTypes(), FeatureResourceType::getCode)); + } + + if (!CollectionUtils.isEmpty(condition.getFeatureIdPairs())) { + wrapper.and(j -> { + for (FeatureIdPair featureIdPair : condition.getFeatureIdPairs()) { + j.or(k -> { + k.in(!CollectionUtils.isEmpty(featureIdPair.getFeatureIds()), SaasProductModuleFeatureRelation::getFeatureId, featureIdPair.getFeatureIds()); + k.eq(Objects.nonNull(featureIdPair.getType()), SaasProductModuleFeatureRelation::getType, featureIdPair.getType()); + }); + } + }); + } + return this.saasProductModuleFeatureRelationDao.list(wrapper); } @@ -233,4 +347,33 @@ public class ProductFeatureRelationServiceImpl implements ProductFeatureRelation public void removeByProductId(Long productId) { saasProductModuleFeatureRelationDao.removeByProductId(Collections.singletonList(productId)); } + + @Override + public SaasProductModuleFeatureRelation getOneByProductId(Long productId) { + List list = saasProductModuleFeatureRelationDao.lambdaQuery() + .eq(SaasProductModuleFeatureRelation::getProductModuleId, productId) + .orderByDesc(BaseEntity::getId) + .last("LIMIT 1") + .list(); + return org.apache.commons.collections4.CollectionUtils.isEmpty(list) ? null : list.get(0); + } + + @Override + public PageResp page(PageProductFeatureRelationReq param) { + QueryWrapper wrapper = QueryWrapperHelper.fromBean(param, SaasProductModuleFeatureRelation.class); + wrapper.eq("is_delete", 0); + + IPage page = this.page(PageConverter.toMybatis(param, SaasProductModuleFeatureRelation.class), wrapper); + + return PageConverter.toResp(page, Function.identity()); + } + + @Override + public List list(PageProductFeatureRelationReq param) { + return PageConverter.drainAll(pageNumber -> { + param.setPage(pageNumber); + param.setPageSize(500); + return page(param); + }); + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductPermissionCacheServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductPermissionCacheServiceImpl.java new file mode 100644 index 00000000..4989fda3 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductPermissionCacheServiceImpl.java @@ -0,0 +1,106 @@ +package cn.axzo.tyr.server.service.impl; + +import cn.axzo.foundation.exception.Axssert; +import cn.axzo.pokonyan.config.redis.RedisClient; +import cn.axzo.tyr.server.service.ProductPermissionCacheService; +import cn.hutool.core.lang.Pair; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Streams; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.data.redis.core.RedisOperations; +import org.springframework.data.redis.core.SessionCallback; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static cn.axzo.tyr.server.config.exception.BizResultCode.REDIS_PRODUCT_NOT_NULL; + +@Slf4j +@Service +public class ProductPermissionCacheServiceImpl implements ProductPermissionCacheService { + + private static final String PRODUCT_PERMISSION_KEY = "product:permission:%s"; + + @Autowired + protected StringRedisTemplate redisTemplate; + + @Override + public List list(ListProductPermissionParam param) { + if (CollectionUtils.isEmpty(param.getProductIds())) { + return Collections.emptyList(); + } + + Set redisKeys = param.getProductIds().stream() + .map(this::getKey) + .collect(Collectors.toSet()); + + Set redisValues = redisTemplate.opsForSet().union(redisKeys); + + return redisValues.stream() + .filter(StringUtils::isNotBlank) + .map(value -> JSONObject.parseObject(value, PermissionDTO.class)) + .collect(Collectors.toList()); + } + + @Override + public void store(StoreProductPermissionParam param) { + + Axssert.check(param.getProductPermissions().stream().allMatch(e -> Objects.nonNull(e.getProductId())), REDIS_PRODUCT_NOT_NULL); + + redisTemplate.executePipelined(new SessionCallback() { + @Override + public Object execute(RedisOperations operations) throws DataAccessException { + + for (ProductPermission productPermission : param.getProductPermissions()) { + String redisKey = getKey(productPermission.getProductId()); + + String[] redisValues = productPermission.getPermissions().stream() + .map(JSONObject::toJSONString) + .toArray(String[]::new); + + RedisClient.SetOps.sAdd(redisKey, redisValues); + redisTemplate.expire(redisKey, 4, TimeUnit.DAYS); + log.info("succeed to store product permission: redisKey:{} value:{}", redisKey, redisValues); + } + return null; + } + }); + } + + @Override + public List hasProductIds(HasProductPermissionParam param) { + List redisValues = redisTemplate.executePipelined(new SessionCallback() { + @Override + public Object execute(RedisOperations operations) throws DataAccessException { + + for (Long productId : param.getProductIds()) { + String redisKey = getKey(productId); + operations.hasKey(redisKey); + } + return null; + } + }); + + return Streams.zip(param.getProductIds().stream(), + redisValues.stream(), + (productId, redisValue) -> Pair.of(productId, redisValue)) + .filter(e -> BooleanUtils.isTrue((Boolean) e.getValue())) + .map(Pair::getKey) + .collect(Collectors.toList()); + } + + private String getKey(Object... params) { + return String.format(PRODUCT_PERMISSION_KEY, params); + } +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductServiceImpl.java index 003d502c..9c3c5fe1 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/ProductServiceImpl.java @@ -2,31 +2,72 @@ package cn.axzo.tyr.server.service.impl; import cn.axzo.basics.common.BeanMapper; import cn.axzo.basics.common.util.AssertUtil; +import cn.axzo.basics.profiles.api.UserProfileServiceApi; +import cn.axzo.basics.profiles.dto.basic.PersonProfileDto; +import cn.axzo.framework.auth.domain.TerminalInfo; import cn.axzo.framework.domain.page.PageResp; import cn.axzo.framework.domain.web.result.ApiPageResult; import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import cn.axzo.tyr.client.common.enums.PermissionRelationOperateLogSceneEnum; +import cn.axzo.tyr.client.common.enums.ProductModuleFeatureRelationTypeEnum; +import cn.axzo.tyr.client.model.dict.request.BasicDictQueryReq; +import cn.axzo.tyr.client.model.dict.response.BasicDictNodeResp; +import cn.axzo.tyr.client.model.enums.ProductModuleCategoryEnum; +import cn.axzo.tyr.client.model.enums.WorkspaceTypeCodeEnum; import cn.axzo.tyr.client.model.product.ProductAddReq; +import cn.axzo.tyr.client.model.product.ProductFeatureRelationSearchReq; +import cn.axzo.tyr.client.model.product.ProductFeatureRelationUpdateReq; +import cn.axzo.tyr.client.model.product.ProductFeatureRelationVO; import cn.axzo.tyr.client.model.product.ProductSearchListReq; import cn.axzo.tyr.client.model.product.ProductSearchPageReq; import cn.axzo.tyr.client.model.product.ProductUpdateReq; import cn.axzo.tyr.client.model.product.ProductVO; -import cn.axzo.tyr.server.repository.entity.ProductModule; +import cn.axzo.tyr.client.model.req.PermissionOperateLogReq; +import cn.axzo.tyr.client.model.req.ProductSaveReq; +import cn.axzo.tyr.client.model.req.UpdateProductStatusReq; +import cn.axzo.tyr.client.model.res.GovernmentTerminalResp; +import cn.axzo.tyr.client.model.res.WorkspaceProductResp; +import cn.axzo.tyr.server.model.RelationOperateLogProductBindResourceDO; import cn.axzo.tyr.server.repository.dao.ProductModuleDao; +import cn.axzo.tyr.server.repository.dao.SaasFeatureResourceDao; +import cn.axzo.tyr.server.repository.entity.ProductModule; +import cn.axzo.tyr.server.repository.entity.SaasFeatureResource; +import cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelationOperateLog; +import cn.axzo.tyr.server.repository.entity.SaasProductModuleFeatureRelation; import cn.axzo.tyr.server.service.ProductFeatureRelationService; import cn.axzo.tyr.server.service.ProductService; -import cn.hutool.core.util.StrUtil; +import cn.axzo.tyr.server.service.SaasBasicDictService; +import cn.axzo.tyr.server.service.SaasFeatureResourceService; +import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationOperateLogService; +import cn.axzo.tyr.server.util.RpcInternalUtil; +import cn.azxo.framework.common.constatns.Constants; +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; -import io.swagger.models.auth.In; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.bouncycastle.util.Integers; +import org.apache.commons.collections4.CollectionUtils; +import org.slf4j.MDC; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; /** @@ -41,6 +82,11 @@ import java.util.stream.Collectors; public class ProductServiceImpl implements ProductService { private final ProductModuleDao productModuleDao; private final ProductFeatureRelationService productFeatureRelationService; + private final SaasFeatureResourceService saasFeatureResourceService; + private final SaasBasicDictService saasBasicDictService; + private final UserProfileServiceApi userProfileServiceApi; + private final SaasPgroupPermissionRelationOperateLogService saasPgroupPermissionRelationOperateLogService; + private final SaasFeatureResourceDao saasFeatureResourceDao; @Override public ApiResult> list(ProductSearchListReq req) { @@ -53,7 +99,7 @@ public class ProductServiceImpl implements ProductService { .eq(Objects.nonNull(req.getAuthType()),ProductModule::getAuthType,req.getAuthType()) .eq(ProductModule::getIsDelete, 0); StringBuilder condition = new StringBuilder(); - if (org.apache.commons.collections.CollectionUtils.isNotEmpty(req.getOuTypes())) { + if (CollectionUtils.isNotEmpty(req.getOuTypes())) { condition = new StringBuilder(); for (Integer value : req.getOuTypes()) { condition.append(" FIND_IN_SET('" + value + "', ou_type) OR"); @@ -77,18 +123,51 @@ public class ProductServiceImpl implements ProductService { IPage page = productModuleDao.lambdaQuery() .like(StringUtils.hasLength(req.getName()), ProductModule::getProductName, req.getName()) .eq(Objects.nonNull(req.getDictWorkspaceTypeId()), ProductModule::getDictWorkspaceTypeId, req.getDictWorkspaceTypeId()) + .eq(Objects.nonNull(req.getDictWorkspaceTypeCode()), ProductModule::getDictWorkspaceTypeCode, req.getDictWorkspaceTypeCode()) .eq(Objects.nonNull(req.getStatus()), ProductModule::getStatus, req.getStatus()) + .eq(org.apache.commons.lang3.StringUtils.isNotBlank(req.getProductCategory()), ProductModule::getCategory, req.getProductCategory()) + .orderByDesc(BaseEntity::getId) .page(req.toPage()); List list = BeanMapper.copyList(page.getRecords(), ProductVO.class); + list.forEach(e -> { + e.setProductCategoryDesc(ProductModuleCategoryEnum.getDescByCode(e.getCategory())); + e.setDictWorkspaceTypeDesc(WorkspaceTypeCodeEnum.getDescByCode(e.getDictWorkspaceTypeCode())); + }); PageResp data = PageResp.list(page.getCurrent(), page.getSize(), page.getTotal(), list); return ApiPageResult.ok(data); } @Override - public ApiResult getById(Long id) { + public ApiResult getById(Long id, Boolean queryFeatureScope) { ProductModule byId = productModuleDao.getById(id); ProductVO productVO = BeanMapper.copyBean(byId, ProductVO.class); productVO.setOuTypes(byId.parseOuType()); + productVO.setProductCategoryDesc(ProductModuleCategoryEnum.getDescByCode(productVO.getCategory())); + productVO.setDictWorkspaceTypeDesc(WorkspaceTypeCodeEnum.getDescByCode(productVO.getDictWorkspaceTypeCode())); + productVO.setSkus(CollectionUtils.isEmpty(byId.getSkus()) ? Collections.emptyList() + : byId.getSkus().stream().map(e -> ProductVO.Sku.builder() + .skuName(e.getSkuName()) + .model(e.getModel()) + .count(e.getCount()) + .unit(e.getUnit()) + .build()).collect(Collectors.toList())); + productVO.setMaterial(Objects.isNull(byId.getMaterial()) ? null : ProductVO.Material.builder() + .images(byId.getMaterial().getImages()) + .videos(byId.getMaterial().getVideos()) + .detailImages(byId.getMaterial().getDetailImages()) + .build()); + if (Boolean.TRUE.equals(queryFeatureScope)) { + if (WorkspaceTypeCodeEnum.GOVERNMENT.getCode().equals(productVO.getDictWorkspaceTypeCode())) { + fillFeatureScope(productVO); + } else { + fillNonGaFeatureRelation(productVO); + } + } else { + SaasProductModuleFeatureRelation relation = productFeatureRelationService.getOneByProductId(id); + if (Objects.nonNull(relation)) { + productVO.setRelationType(relation.getType()); + } + } return ApiResult.ok(productVO); } @@ -124,9 +203,25 @@ public class ProductServiceImpl implements ProductService { productModule.setOuType(org.apache.commons.lang3.StringUtils.join(req.getOuTypes(),",")); } productModuleDao.updateById(productModule); + try { + saveOperateLogForUpdateProduct(req, productModule); + } catch (Exception e) { + log.warn("error save operate log", e); + } return ApiResult.ok(BeanMapper.copyBeanIgnoreNull(productModule, ProductVO.class)); } + private void saveOperateLogForUpdateProduct(ProductUpdateReq req, ProductModule productModule) { + PermissionOperateLogReq log = new PermissionOperateLogReq(); + log.setTableName(SaasPgroupPermissionRelationOperateLogServiceImpl.TABLE_NAME_PRODUCT); + log.setScene(PermissionRelationOperateLogSceneEnum.OLD_OMS_PRODUCT__UPDATE.getValue()); + log.setSceneId(productModule.getId() + ""); + log.setOperatorId(req.determineOperator().getPersonId()); + log.setRequestData(req); + log.setOperateData(productModule); + saasPgroupPermissionRelationOperateLogService.save(log); + } + @Override @Transactional(rollbackFor = Exception.class) public ApiResult delete(Long id) { @@ -141,4 +236,338 @@ public class ProductServiceImpl implements ProductService { productFeatureRelationService.removeByProductId(id); return ApiResult.ok(BeanMapper.copyBean(productModule, ProductVO.class)); } + + @Override + public ApiResult updateStatus(UpdateProductStatusReq req) { + ProductModule productModule = productModuleDao.getOne(new LambdaQueryWrapper() + .eq(BaseEntity::getId, req.getId()) + .eq(BaseEntity::getIsDelete, Boolean.FALSE) + .last("LIMIT 1")); + AssertUtil.notNull(productModule, "产品不存在"); + productModuleDao.lambdaUpdate() + .eq(ProductModule::getId, req.getId()) + .set(ProductModule::getStatus, req.getStatus()) + .update(); + return ApiResult.ok(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ApiResult saveOrUpdate(ProductSaveReq req) { + ProductModule productModule = new ProductModule(); + if (Objects.nonNull(req.getId())) { + productModule = productModuleDao.getOne(new LambdaQueryWrapper() + .eq(BaseEntity::getId, req.getId()) + .eq(BaseEntity::getIsDelete, Boolean.FALSE) + .last("LIMIT 1")); + AssertUtil.notNull(productModule, "产品不存在"); + if (Objects.nonNull(req.getVersion()) && Objects.nonNull(productModule.getVersion())) { + AssertUtil.isTrue(req.getVersion() >= productModule.getVersion(), "版本升级序列不允许降级"); + } + } + + validAndFillEntity(req, productModule); + + if (Objects.nonNull(req.getId())) { + productModule.setUpdateBy(req.getOperator()); + productModuleDao.updateById(productModule); + } else { + productModule.setCreateBy(req.getOperator()); + productModuleDao.save(productModule); + } + // 保存商品权限信息 + if (WorkspaceTypeCodeEnum.GOVERNMENT.getCode().equals(productModule.getDictWorkspaceTypeCode())) { + saveGovernmentFeatureResource(productModule.getId(), productModule.getDictWorkspaceTypeId(), productModule.getDictWorkspaceTypeCode(), req.getFeatureScope().getGovernmentFeatureResourceIds(), req); + } else { + saveNonGaFeatureResource(productModule.getId(), req.getFeatureScope().getRelations()); + // 保存操作日志 + try { + saveOperateLog4NonGaProduct(req, productModule.getId()); + } catch (Exception e) { + log.warn("save operate log error", e); + } + } + return ApiResult.ok(productModule.getId()); + } + + @Override + public ApiResult> getGovernmentTerminal(String terminal) { + List featureResources = saasFeatureResourceService.listByParentIdAndTerminalAndIds(0L, terminal, null); + List resps = CollectionUtil.isEmpty(featureResources) ? Collections.emptyList() : featureResources.stream().map(e -> GovernmentTerminalResp.builder() + .featureResourceId(e.getId()) + .featureResourceName(e.getFeatureName()) + .build()).collect(Collectors.toList()); + return ApiResult.ok(resps); + } + + @Override + public ApiResult getWorkspaceProduct(String workspaceType) { + List productModules = productModuleDao.lambdaQuery() + .eq(ProductModule::getStatus, 1) + .eq(Objects.nonNull(workspaceType), ProductModule::getDictWorkspaceTypeCode, workspaceType) + .in(ProductModule::getCategory, ProductModuleCategoryEnum.ALL_CATEGORY_CODE) + .list(); + if (CollectionUtil.isEmpty(productModules)) { + return ApiResult.ok(WorkspaceProductResp.builder().build()); + } + + WorkspaceProductResp resp = WorkspaceProductResp.init(); + Map> categoryMap = productModules.stream().collect(Collectors.groupingBy(ProductModule::getCategory)); + categoryMap.forEach((k,v) -> { + // 政务产品 + if (WorkspaceTypeCodeEnum.GOVERNMENT.getCode().equals(workspaceType)) { + resp.getGovernmentProduct().addAll(v.stream().map(e -> WorkspaceProductResp.Product.builder() + .productId(e.getId()) + .productName(e.getProductName()) + .build()).collect(Collectors.toList())); + } else { + if (ProductModuleCategoryEnum.PRODUCT_VERSION.getCode().equals(k)) { + List products = v.stream().map(this::buildRespProduct).filter(Objects::nonNull).collect(Collectors.toList()); + if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(products)) { + products.sort(Comparator.comparing(p -> Objects.isNull(p.getVersion()) ? 0 : p.getVersion())); + } + resp.getProductVersions().addAll(products); + } + if (ProductModuleCategoryEnum.ADD_VALUE_SERVICE.getCode().equals(k)) { + resp.getAddValueServices().addAll(v.stream().map(this::buildRespProduct).filter(Objects::nonNull).collect(Collectors.toList())); + } + if (ProductModuleCategoryEnum.GENERAL_SERVICE.getCode().equals(k)) { + resp.getGeneralServices().addAll(v.stream().map(this::buildRespProduct).filter(Objects::nonNull).collect(Collectors.toList())); + } + if (ProductModuleCategoryEnum.HARD_WARE.getCode().equals(k)) { + resp.getHardware().addAll(v.stream().map(this::buildRespProduct).filter(Objects::nonNull).collect(Collectors.toList())); + } + } + }); + + return ApiResult.ok(resp); + } + + private void validAndFillEntity(ProductSaveReq req, ProductModule productModule) { + ProductModule nameProduct = productModuleDao.getOne(new LambdaQueryWrapper() + .eq(BaseEntity::getIsDelete, Boolean.FALSE) + .eq(ProductModule::getProductName, req.getProductName()) + .eq(ProductModule::getDictWorkspaceTypeId, req.getDictWorkspaceTypeId()) + .ne(Objects.nonNull(req.getId()), BaseEntity::getId, req.getId()) + .last("LIMIT 1")); + AssertUtil.isNull(nameProduct, "产品【" + req.getProductName() + "】已存在"); + + BasicDictNodeResp basicDictNodeResp = saasBasicDictService.getById(req.getDictWorkspaceTypeId()); + AssertUtil.notNull(basicDictNodeResp, "租户类型有误"); + WorkspaceTypeCodeEnum workspaceTypeCodeEnum = WorkspaceTypeCodeEnum.getByCode(basicDictNodeResp.getCode()); + AssertUtil.notNull(workspaceTypeCodeEnum, "租户类型有误"); + AssertUtil.isTrue(WorkspaceTypeCodeEnum.ALLOW_ADD_WORKSPACE_TYPE_CODE_ENUM.contains(workspaceTypeCodeEnum), "租户类型选择有误"); + + ProductModuleCategoryEnum productModuleCategoryEnum = ProductModuleCategoryEnum.getByCode(req.getProductCategory()); + AssertUtil.notNull(productModuleCategoryEnum, "产品类型有误"); + + if (WorkspaceTypeCodeEnum.GOVERNMENT.equals(workspaceTypeCodeEnum)) { + AssertUtil.isTrue(Objects.nonNull(req.getFeatureScope()) + && CollectionUtils.isNotEmpty(req.getFeatureScope().getGovernmentFeatureResourceIds()), "功能范围选择有误"); + } else { + if (Objects.isNull(req.getId())) { + AssertUtil.isTrue(Objects.nonNull(req.getFeatureScope()) + && CollectionUtils.isNotEmpty(req.getFeatureScope().getRelations()), "产品的功能范围选择有误"); + } else { +// SaasProductModuleFeatureRelation oneRelation = productFeatureRelationService.getOneByProductId(req.getId()); +// AssertUtil.isTrue(Objects.isNull(oneRelation) +// || ProductModuleFeatureRelationTypeEnum.FEATURE_RESOURCE.getCode().equals(oneRelation.getType()), "请在老的产品管理菜单编辑产品权限。"); + } + } + productModule.setProductName(req.getProductName()); + productModule.setIcon(req.getIcon()); + productModule.setDictWorkspaceTypeId(req.getDictWorkspaceTypeId()); + productModule.setDictWorkspaceTypeCode(workspaceTypeCodeEnum.getCode()); + productModule.setProductType(Objects.isNull(productModule.getProductType()) ? 0 : productModule.getProductType()); + productModule.setStatus(Objects.nonNull(productModule.getId()) ? productModule.getStatus() : 1); + productModule.setCategory(req.getProductCategory()); + productModule.setVersion(req.getVersion()); + productModule.setMaxPersonCount(req.getMaxPersonCount()); + productModule.setMaxWorkspaceCount(req.getMaxWorkspaceCount()); + productModule.setPrice(req.getPrice()); + productModule.setSkus(CollectionUtils.isEmpty(req.getSkus()) ? Lists.newArrayList() + : req.getSkus().stream().map(e -> ProductModule.Sku.builder() + .skuName(e.getSkuName()) + .model(e.getModel()) + .count(e.getCount()) + .unit(e.getUnit()) + .build()).collect(Collectors.toList())); + productModule.setMaterial(Optional.ofNullable(req.getMaterial()).map(e -> ProductModule.Material.builder() + .images(e.getImages()) + .videos(e.getVideos()) + .detailImages(e.getDetailImages()) + .build()).orElse(ProductModule.Material.builder().build())); + productModule.setRemark(req.getRemark()); + } + + /** + * 保存政务端产品功能权限 + */ + private void saveGovernmentFeatureResource(Long productId, Long dictWorkspaceTypeId, String dictWorkspaceTypeCode, List rootFeatureIds, ProductSaveReq request) { + List saasFeatureResources = saasFeatureResourceService.listByParentIdAndTerminalAndIds(null, null, rootFeatureIds); + AssertUtil.notEmpty(saasFeatureResources, "功能范围选择端不能为空"); + saasFeatureResources.forEach(e -> { + AssertUtil.isTrue(e.getParentId() == 0 && TerminalInfo.NT_PC_GA_GENERAL.equals(e.getTerminal()), "功能范围选择端有误"); + }); + + List frs = saasFeatureResourceService.batchListDescendant(rootFeatureIds); + ProductFeatureRelationUpdateReq req = new ProductFeatureRelationUpdateReq(); + req.setDictCodeId(dictWorkspaceTypeId); + req.setDictCode(dictWorkspaceTypeCode); + req.setProductModuleId(productId); + Set featureIds = CollectionUtils.isEmpty(frs) ? Sets.newHashSet() + : frs.stream().map(BaseEntity::getId).collect(Collectors.toSet()); + featureIds.addAll(rootFeatureIds); + req.getFeatureIds().addAll(featureIds); + req.setFeatureRelationType(ProductModuleFeatureRelationTypeEnum.FEATURE_RESOURCE.getCode()); + productFeatureRelationService.updateFeatureResourceRelation(Collections.singletonList(req), ProductModuleFeatureRelationTypeEnum.FEATURE_RESOURCE.getCode()); + + // 保存操作日志 + try { + saveOperateLog4GovernmentProduct(request, productId, frs); + } catch (Exception e) { + log.warn("save operate log error", e); + } + } + + /** + * 保存非政务端产品功能权限 + */ + private void saveNonGaFeatureResource(Long productId, List relations) { + if (CollectionUtils.isEmpty(relations)) { + productFeatureRelationService.removeFeatureResourceRelationByProductIdAndType(productId, ProductModuleFeatureRelationTypeEnum.FEATURE_RESOURCE.getCode()); + return; + } + List dictCodeIds = Lists.newArrayList(); + for (ProductSaveReq.ProductFeatureRelation relation : relations) { + AssertUtil.notNull(relation.getDictCodeId(), "功能范围选择的字典ID不能未空。"); + dictCodeIds.add(relation.getDictCodeId()); + } + + Map nodeMap = saasBasicDictService.getBasicDictNodeList(BasicDictQueryReq.builder().ids(dictCodeIds).build()) + .stream().collect(Collectors.toMap(BasicDictNodeResp::getId, Function.identity(), (v1, v2) -> v1)); + + List reqs = Lists.newArrayList(); + for (ProductSaveReq.ProductFeatureRelation relation : relations) { + ProductFeatureRelationUpdateReq req = new ProductFeatureRelationUpdateReq(); + req.setDictCodeId(relation.getDictCodeId()); + req.setDictCode(nodeMap.getOrDefault(relation.getDictCodeId(), new BasicDictNodeResp()).getCode()); + req.setProductModuleId(productId); + if (CollectionUtils.isNotEmpty(relation.getFeatureIds())) { + req.getFeatureIds().addAll(relation.getFeatureIds()); + } + req.setFeatureRelationType(ProductModuleFeatureRelationTypeEnum.FEATURE_RESOURCE.getCode()); + reqs.add(req); + } + productFeatureRelationService.updateFeatureResourceRelation(reqs, ProductModuleFeatureRelationTypeEnum.FEATURE_RESOURCE.getCode()); + } + + private void fillFeatureScope(ProductVO product) { + ProductFeatureRelationSearchReq req = new ProductFeatureRelationSearchReq(); + req.setProductModuleId(product.getId()); + req.setDictCodeId(product.getDictWorkspaceTypeId()); + req.setDictCode(product.getDictWorkspaceTypeCode()); + ApiResult> result = productFeatureRelationService.featureList(req); + if (CollectionUtils.isEmpty(result.getData())) { + return; + } + product.setRelationType(result.getData().get(0).getType()); + List featureIds = result.getData().stream().map(ProductFeatureRelationVO::getFeatureId).collect(Collectors.toList()); + List featureResources = saasFeatureResourceService.listByParentIdAndTerminalAndIds(0L, TerminalInfo.NT_PC_GA_GENERAL, featureIds); + if (CollectionUtil.isNotEmpty(featureResources)) { + product.setFeatureScopes(featureResources.stream().map(e -> ProductVO.FeatureScope.builder() + .governmentFeatureResourceId(e.getId()) + .featureResourceName(e.getFeatureName()) + .build()).collect(Collectors.toList())); + } + } + + private void fillNonGaFeatureRelation(ProductVO product) { + ProductFeatureRelationSearchReq req = new ProductFeatureRelationSearchReq(); + req.setProductModuleId(product.getId()); + req.setType(ProductModuleFeatureRelationTypeEnum.FEATURE_RESOURCE.getCode()); + ApiResult> result = productFeatureRelationService.featureList(req); + if (CollectionUtils.isEmpty(result.getData())) { + return; + } + product.setRelationType(result.getData().get(0).getType()); + + Map> relationMap = result.getData().stream().collect(Collectors.groupingBy(ProductFeatureRelationVO::getDictCodeId)); + List relations = Lists.newArrayList(); + relationMap.forEach((k, v) -> { + ProductVO.ProductFeatureRelation pr = ProductVO.ProductFeatureRelation.builder() + .dictCodeId(k) + .totalCount(v.size()) + .featureIds(v.stream().map(ProductFeatureRelationVO::getFeatureId).collect(Collectors.toList())) + .build(); + relations.add(pr); + }); + product.setRelations(relations); + } + + private WorkspaceProductResp.Product buildRespProduct(ProductModule pm) { + return Optional.ofNullable(pm).map(e -> WorkspaceProductResp.Product.builder() + .productId(e.getId()) + .productName(e.getProductName()) + .maxPersonCount(e.getMaxPersonCount()) + .maxWorkspaceCount(e.getMaxWorkspaceCount()) + .version(e.getVersion()) + .skus(CollectionUtil.isNotEmpty(e.getSkus()) ? e.getSkus().stream().map(s -> WorkspaceProductResp.Sku + .builder() + .skuName(s.getSkuName()) + .model(s.getModel()) + .count(s.getCount()) + .unit(s.getUnit()) + .build()).collect(Collectors.toList()) : Collections.emptyList()) + .build()).orElse(null); + } + + private void saveOperateLog4GovernmentProduct(ProductSaveReq req, Long productId, List featureResources) { + List personProfileDtos = RpcInternalUtil.rpcListProcessor(() -> userProfileServiceApi.getPersonProfiles(Lists.newArrayList(req.getOperator())), "查询用户信息", req.getOperator()).getData(); + PersonProfileDto operator = CollectionUtils.isEmpty(personProfileDtos) ? null : personProfileDtos.get(0); + + RelationOperateLogProductBindResourceDO operateAfter = RelationOperateLogProductBindResourceDO.builder() + .productId(productId) + .uniCodes(CollectionUtils.emptyIfNull(featureResources).stream().map(SaasFeatureResource::getUniCode).collect(Collectors.toList())) + .build(); + SaasPgroupPermissionRelationOperateLog operateLog = SaasPgroupPermissionRelationOperateLog.builder() + .tableName(SaasPgroupPermissionRelationOperateLogServiceImpl.TABLE_NAME_PRODUCT) + .scene(PermissionRelationOperateLogSceneEnum.OMS_PRODUCT_BIND_FEATURE_RESOURCE.getValue()) + .sceneId(productId.toString()) + .createBy(req.getOperator()) + .createByName(Objects.isNull(operator) ? "" : operator.getRealName()) + .traceId(MDC.get(Constants.CTX_LOG_ID_MDC)) + .requestData(JSONObject.toJSONString(req)) + .operateData(JSONObject.toJSONString(Lists.newArrayList(operateAfter))) + .createByRole(JSONObject.toJSONString(saasPgroupPermissionRelationOperateLogService.getPersonBasicRoles(req.getOperator()))) + .build(); + saasPgroupPermissionRelationOperateLogService.batchSave(Lists.newArrayList(operateLog)); + } + + private void saveOperateLog4NonGaProduct(ProductSaveReq req, Long productId) { + List personProfileDtos = RpcInternalUtil.rpcListProcessor(() -> userProfileServiceApi.getPersonProfiles(Lists.newArrayList(req.getOperator())), "查询用户信息", req.getOperator()).getData(); + PersonProfileDto operator = CollectionUtils.isEmpty(personProfileDtos) ? null : personProfileDtos.get(0); + List featureResources = Lists.newArrayList(); + if (CollectionUtils.isNotEmpty(req.getFeatureScope().getRelations())) { + Set allFeatureResourceIds = req.getFeatureScope().getRelations().stream().map(ProductSaveReq.ProductFeatureRelation::getFeatureIds).filter(CollectionUtils::isNotEmpty).flatMap(Set::stream).collect(Collectors.toSet()); + featureResources = saasFeatureResourceDao.lambdaQuery().in(BaseEntity::getId, allFeatureResourceIds).list(); + } + + RelationOperateLogProductBindResourceDO operateAfter = RelationOperateLogProductBindResourceDO.builder() + .productId(productId) + .uniCodes(featureResources.stream().map(SaasFeatureResource::getUniCode).collect(Collectors.toList())) + .build(); + SaasPgroupPermissionRelationOperateLog operateLog = SaasPgroupPermissionRelationOperateLog.builder() + .tableName(SaasPgroupPermissionRelationOperateLogServiceImpl.TABLE_NAME_PRODUCT) + .scene(PermissionRelationOperateLogSceneEnum.OMS_PRODUCT_BIND_FEATURE_RESOURCE.getValue()) + .sceneId(productId.toString()) + .createBy(req.getOperator()) + .createByName(Objects.isNull(operator) ? "" : operator.getRealName()) + .traceId(MDC.get(Constants.CTX_LOG_ID_MDC)) + .requestData(JSONObject.toJSONString(req)) + .operateData(JSONObject.toJSONString(Lists.newArrayList(operateAfter))) + .createByRole(JSONObject.toJSONString(saasPgroupPermissionRelationOperateLogService.getPersonBasicRoles(req.getOperator()))) + .build(); + saasPgroupPermissionRelationOperateLogService.batchSave(Lists.newArrayList(operateLog)); + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RolePermissionCacheServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RolePermissionCacheServiceImpl.java new file mode 100644 index 00000000..455175da --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RolePermissionCacheServiceImpl.java @@ -0,0 +1,106 @@ +package cn.axzo.tyr.server.service.impl; + +import cn.axzo.foundation.exception.Axssert; +import cn.axzo.pokonyan.config.redis.RedisClient; +import cn.axzo.tyr.server.service.RolePermissionCacheService; +import cn.hutool.core.lang.Pair; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Streams; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.data.redis.core.RedisCallback; +import org.springframework.data.redis.core.RedisOperations; +import org.springframework.data.redis.core.SessionCallback; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static cn.axzo.tyr.server.config.exception.BizResultCode.REDIS_ROLE_NOT_NULL; + +@Slf4j +@Service +public class RolePermissionCacheServiceImpl implements RolePermissionCacheService { + + private static final String ROLE_PERMISSION_KEY = "role:permission:%s"; + + @Autowired + protected StringRedisTemplate redisTemplate; + + @Override + public List list(ListRolePermissionParam param) { + if (CollectionUtils.isEmpty(param.getRoleIds())) { + return Collections.emptyList(); + } + + Set redisKeys = param.getRoleIds().stream() + .map(this::getKey) + .collect(Collectors.toSet()); + + // 聚合是因为角色的权限点有重复,网络io压力大 + Set redisValues = redisTemplate.opsForSet().union(redisKeys); + return redisValues.stream() + .filter(StringUtils::isNotBlank) + .map(value -> JSONObject.parseObject(value, PermissionDTO.class)) + .collect(Collectors.toList()); + } + + @Override + public void store(StoreRolePermissionParam param) { + + Axssert.check(param.getRolePermissions().stream().allMatch(e -> Objects.nonNull(e.getRoleId())), REDIS_ROLE_NOT_NULL); + + redisTemplate.executePipelined((RedisCallback) connection -> { + + connection.openPipeline(); + + for (RolePermission rolePermission : param.getRolePermissions()) { + String redisKey = getKey(rolePermission.getRoleId()); + + String[] redisValues = rolePermission.getPermissions().stream() + .map(JSONObject::toJSONString) + .toArray(String[]::new); + + RedisClient.SetOps.sAdd(redisKey, redisValues); + redisTemplate.expire(redisKey, 4, TimeUnit.DAYS); + log.info("succeed to store role permission: redisKey:{} value:{}", redisKey, redisValues); + } + return null; + }); + } + + @Override + public List hasRoleIds(HasRolePermissionParam param) { + List redisValues = redisTemplate.executePipelined(new SessionCallback() { + @Override + public Object execute(RedisOperations operations) throws DataAccessException { + + for (Long roleId : param.getRoleIds()) { + String redisKey = getKey(roleId); + operations.hasKey(redisKey); + } + return null; + } + }); + + return Streams.zip(param.getRoleIds().stream(), + redisValues.stream(), + (roleId, redisValue) -> Pair.of(roleId, redisValue)) + .filter(e -> BooleanUtils.isTrue((Boolean) e.getValue())) + .map(Pair::getKey) + .collect(Collectors.toList()); + } + + private String getKey(Object... params) { + return String.format(ROLE_PERMISSION_KEY, params); + } +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RoleServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RoleServiceImpl.java index ead5ac42..e8af5f13 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RoleServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RoleServiceImpl.java @@ -2,17 +2,20 @@ package cn.axzo.tyr.server.service.impl; import cn.axzo.basics.common.constant.enums.TableIsDeleteEnum; import cn.axzo.basics.common.exception.ServiceException; +import cn.axzo.basics.profiles.api.UserProfileServiceApi; +import cn.axzo.basics.profiles.dto.basic.PersonProfileDto; +import cn.axzo.foundation.dao.support.converter.PageConverter; +import cn.axzo.foundation.dao.support.mysql.QueryWrapperHelper; import cn.axzo.framework.domain.page.PageResp; import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; -import cn.axzo.pokonyan.dao.converter.PageConverter; -import cn.axzo.pokonyan.dao.mysql.QueryWrapperHelper; +import cn.axzo.tyr.client.common.enums.PermissionRelationOperateLogSceneEnum; import cn.axzo.tyr.client.common.enums.RoleResourceTypeEnum; import cn.axzo.tyr.client.common.enums.RoleTypeEnum; import cn.axzo.tyr.client.model.enums.IdentityType; import cn.axzo.tyr.client.model.enums.PermissionGroupType; -import cn.axzo.tyr.client.model.permission.PermissionPointTreeNode; import cn.axzo.tyr.client.model.req.ChangeGroupLeaderRoleReq; import cn.axzo.tyr.client.model.req.FeatureRoleRelationReq; +import cn.axzo.tyr.client.model.req.PagePgroupPermissionRelationReq; import cn.axzo.tyr.client.model.req.QueryByIdentityIdTypeReq; import cn.axzo.tyr.client.model.req.QueryRoleByNameReq; import cn.axzo.tyr.client.model.req.QuerySaasPermissionGroupReq; @@ -24,54 +27,37 @@ import cn.axzo.tyr.client.model.res.IsSuperAdminRes; import cn.axzo.tyr.client.model.res.QueryBatchByIdentityIdTypeRes; import cn.axzo.tyr.client.model.res.QueryRoleByNameResp; import cn.axzo.tyr.client.model.res.RoleWithUserRes; +import cn.axzo.tyr.client.model.res.SaasPermissionRelationRes; import cn.axzo.tyr.client.model.res.SaasPermissionRes; import cn.axzo.tyr.client.model.res.SaasRoleGroupRes; import cn.axzo.tyr.client.model.res.SaasRoleRes; +import cn.axzo.tyr.client.model.roleuser.dto.SaasRoleUserV2DTO; +import cn.axzo.tyr.client.model.roleuser.req.ListRoleUserRelationParam; import cn.axzo.tyr.client.model.vo.DeleteRoleVO; import cn.axzo.tyr.client.model.vo.SaasPermissionGroupVO; import cn.axzo.tyr.client.model.vo.SaasRoleAndGroupVO; import cn.axzo.tyr.client.model.vo.SaasRoleCategoryVO; import cn.axzo.tyr.client.model.vo.SaasRoleGroupCodeVO; -import cn.axzo.tyr.client.model.vo.SaasRoleGroupVO; import cn.axzo.tyr.client.model.vo.SaasRoleVO; import cn.axzo.tyr.client.model.vo.SaveOrUpdateRoleVO; -import cn.axzo.tyr.server.model.ResourcePermission; -import cn.axzo.tyr.server.model.ResourcePermissionQueryDTO; -import cn.axzo.tyr.server.model.RoleFeatureRelation; -import cn.axzo.tyr.server.model.RoleWithFeature; -import cn.axzo.tyr.server.repository.dao.SaasFeatureDao; -import cn.axzo.tyr.server.repository.dao.SaasPermissionGroupDao; -import cn.axzo.tyr.server.repository.dao.SaasPgroupPermissionRelationDao; -import cn.axzo.tyr.server.repository.dao.SaasPgroupRoleRelationDao; -import cn.axzo.tyr.server.repository.dao.SaasRoleDao; -import cn.axzo.tyr.server.repository.dao.SaasRoleGroupDao; -import cn.axzo.tyr.server.repository.dao.SaasRoleGroupRelationDao; -import cn.axzo.tyr.server.repository.dao.SaasRoleUserRelationDao; -import cn.axzo.tyr.server.repository.entity.SaasFeature; -import cn.axzo.tyr.server.repository.entity.SaasFeatureResource; -import cn.axzo.tyr.server.repository.entity.SaasPermissionGroup; -import cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation; -import cn.axzo.tyr.server.repository.entity.SaasPgroupRoleRelation; -import cn.axzo.tyr.server.repository.entity.SaasRole; -import cn.axzo.tyr.server.repository.entity.SaasRoleGroup; -import cn.axzo.tyr.server.repository.entity.SaasRoleGroupRelation; -import cn.axzo.tyr.server.repository.entity.SaasRoleUserRelation; -import cn.axzo.tyr.server.repository.entity.SaasRoleWithUser; +import cn.axzo.tyr.server.model.*; +import cn.axzo.tyr.server.repository.dao.*; +import cn.axzo.tyr.server.repository.entity.*; import cn.axzo.tyr.server.repository.mapper.SaasRoleMapper; -import cn.axzo.tyr.server.service.PermissionGroupService; -import cn.axzo.tyr.server.service.RoleService; -import cn.axzo.tyr.server.service.SaasFeatureResourceService; -import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationService; -import cn.axzo.tyr.server.service.SaasRoleGroupRelationService; -import cn.axzo.tyr.server.service.SaasRoleGroupService; +import cn.axzo.tyr.server.service.*; +import cn.axzo.tyr.server.util.RpcInternalUtil; +import cn.azxo.framework.common.constatns.Constants; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.lang.Assert; +import cn.hutool.core.lang.Pair; import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.google.common.collect.Lists; @@ -85,6 +71,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; +import org.slf4j.MDC; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -97,7 +84,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -107,6 +93,9 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.NEW_FEATURE; +import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.OLD_FEATURE; + /** * 角色 * @@ -154,6 +143,15 @@ public class RoleServiceImpl extends ServiceImpl SaasRoleGroupRelationDao saasRoleGroupRelationDao; @Autowired SaasFeatureResourceService saasFeatureResourceService; + @Autowired + private SaasRoleUserRelationService saasRoleUserRelationService; + @Autowired + private SaasPgroupPermissionRelationOperateLogService saasPgroupPermissionRelationOperateLogService; + @Autowired + private UserProfileServiceApi userProfileServiceApi; + @Autowired + private SaasFeatureResourceDao saasFeatureResourceDao; + @Value("${groupLeader.code:projectTeamGPLeader}") private String groupLeaderCode; @@ -332,9 +330,9 @@ public class RoleServiceImpl extends ServiceImpl SaasPermissionGroup saasPermissionGroup = validPermissionGroupCommon(saveOrUpdateRole); // TODO 旧的权限,待权限切完后就下掉 - validFeature(saveOrUpdateRole.getSelectedPPIds()); + List saasFeatures = validFeature(saveOrUpdateRole.getSelectedPPIds()); // 新的权限 - validPermission(saveOrUpdateRole.getPermissionIds()); + List saasFeatureResources = validPermission(saveOrUpdateRole.getPermissionIds()); saasRoleDao.saveOrUpdate(saasRole); // 新增或者保存分组和角色映射关系 @@ -358,71 +356,126 @@ public class RoleServiceImpl extends ServiceImpl if (Objects.isNull(saveOrUpdateRole.getId())) { saasPgroupRoleRelationDao.save(pgrr); } - if (CollectionUtils.isNotEmpty(saveOrUpdateRole.getSelectedPPIds()) - || CollectionUtils.isNotEmpty(saveOrUpdateRole.getPermissionIds())) { - List pids = Optional.ofNullable(saveOrUpdateRole.getSelectedPPIds()) - .orElseGet(() -> Lists.newArrayList(saveOrUpdateRole.getPermissionIds())); + // 还有CMP和CM使用旧的菜单资源树 + saveSaasFeaturePermission(saveOrUpdateRole, saasFeatures, saasPermissionGroup); - // 保存权限集和权限点映射关系 - List pgpRelations = pids.stream().map(ppId -> { - SaasPgroupPermissionRelation target = new SaasPgroupPermissionRelation(); - target.setGroupId(saasPermissionGroup.getId()); - target.setFeatureId(ppId); - target.setCreateBy(saveOrUpdateRole.getOperatorId()); - target.setUpdateBy(saveOrUpdateRole.getOperatorId()); - return target; - }).collect(Collectors.toList()); - saasPgroupPermissionRelationService.saveOrUpdate(Lists.newArrayList(saasPermissionGroup.getId()), pgpRelations); - } + // 保存新的菜单资源树权限 + saveSaasFeatureResourcePermission(saveOrUpdateRole, saasFeatureResources, saasPermissionGroup); return saasRole.getId(); } - private void validRoleName(SaveOrUpdateRoleVO saveOrUpdateRole) { - List groupTrees = saveOrUpdateRole.getGroupTree(); - if (CollectionUtil.isEmpty(groupTrees)) { + /** + * 后续cms,cmp的菜单切到saas_feature_resource后,就去掉这个方法 + * @param saveOrUpdateRole + * @param saasFeatures + * @param saasPermissionGroup + */ + private void saveSaasFeaturePermission(SaveOrUpdateRoleVO saveOrUpdateRole, + List saasFeatures, + SaasPermissionGroup saasPermissionGroup) { + if (CollectionUtils.isEmpty(saveOrUpdateRole.getSelectedPPIds())) { return; } - String currentWorkspaceCode = groupTrees.get(0).getWorkspaceTypeCode(); - List roleGroups = saasRoleGroupDao.lambdaQuery() - .in(SaasRoleGroup::getWorkspaceTypeCode, currentWorkspaceCode) - .eq(SaasRoleGroup::getWorkspaceId, saveOrUpdateRole.getWorkspaceId()) - .eq(SaasRoleGroup::getOuId, saveOrUpdateRole.getOwnerOuId()) - .eq(SaasRoleGroup::getIsDelete, TableIsDeleteEnum.NORMAL.value).list(); - List roleGroupIds = roleGroups.stream().map(SaasRoleGroup::getId).collect(Collectors.toList()); - if (CollectionUtil.isEmpty(roleGroupIds)) { - return; - } - List roleGroupRelations = roleGroupRelationDao.lambdaQuery() - .in(SaasRoleGroupRelation::getSaasRoleGroupId, roleGroupIds) - .eq(SaasRoleGroupRelation::getIsDelete, TableIsDeleteEnum.NORMAL.value).list(); - if (CollectionUtil.isNotEmpty(roleGroupRelations)) { - List saasRoleIds = roleGroupRelations.stream().map(SaasRoleGroupRelation::getRoleId).collect(Collectors.toList()); - //确保这些角色id 都是正常使用的 - List roles = saasRoleDao.lambdaQuery() - .eq(SaasRole::getWorkspaceId, saveOrUpdateRole.getWorkspaceId()) - .eq(SaasRole::getId, saasRoleIds) - .eq(SaasRole::getName, saveOrUpdateRole.getName()) - .eq(SaasRole::getOwnerOuId, saveOrUpdateRole.getOwnerOuId()) - .eq(SaasRole::getIsDelete, TableIsDeleteEnum.NORMAL.value).list(); - if (CollectionUtil.isNotEmpty(roles)) { - //新增角色 判断角色名称重复 - if (Objects.isNull(saveOrUpdateRole.getId())) { - throw new ServiceException("同一企业单位、同一工作台类型,角色名称不能重复!"); - } else { - //如果是更新角色,必须是当前角色 - if (!(roles.size() == 1 && (roles.get(0).getId().equals(saveOrUpdateRole.getId())))) { - throw new ServiceException("同一企业单位、同一工作台类型,角色名称不能重复!"); - } - } - } -// Optional repeatGroupName = roles.stream() -// .filter(g -> !Objects.equals(g.getId(), saveOrUpdateRole.getId()) && StringUtils.equalsIgnoreCase(g.getName(), saveOrUpdateRole.getName())).findFirst(); -// if (repeatGroupName.isPresent()) { -// throw new ServiceException("同一个单位、同一工作台类型内,角色名称不能重复!"); -// } + Map saasFeatureMap = saasFeatures.stream() + .collect(Collectors.toMap(SaasFeature::getId, SaasFeature::getFeatureType)); + + // 保存权限集和权限点映射关系 + List pgpRelations = saveOrUpdateRole.getSelectedPPIds().stream().map(ppId -> { + SaasPgroupPermissionRelation target = new SaasPgroupPermissionRelation(); + target.setGroupId(saasPermissionGroup.getId()); + target.setFeatureId(ppId); + target.setCreateBy(saveOrUpdateRole.getOperatorId()); + target.setUpdateBy(saveOrUpdateRole.getOperatorId()); + target.setFeatureType(saasFeatureMap.get(ppId)); + target.setType(OLD_FEATURE); + return target; + }).collect(Collectors.toList()); + SaasPgroupPermissionRelationService.UpsertPermissionRelationParam upsertPermissionRelationParam = SaasPgroupPermissionRelationService.UpsertPermissionRelationParam.builder() + .groupIds(Lists.newArrayList(saasPermissionGroup.getId())) + .relations(pgpRelations) + .type(OLD_FEATURE) + .build(); + saasPgroupPermissionRelationService.saveOrUpdate(upsertPermissionRelationParam); + try { + saveOperateLog4RoleBindFeature(saveOrUpdateRole); + } catch (Exception e) { + log.warn("save operate log error", e); + } + } + + private void saveOperateLog4RoleBindFeature(SaveOrUpdateRoleVO saveOrUpdateRole) { + SaasRole saasRole = saasRoleDao.getById(saveOrUpdateRole.getId()); + List uniCodes = null; + if (CollectionUtils.isNotEmpty(saveOrUpdateRole.getPermissionIds())) { + uniCodes = saasFeatureResourceDao.lambdaQuery().in(BaseEntity::getId, saveOrUpdateRole.getPermissionIds()).list().stream().map(SaasFeatureResource::getUniCode).collect(Collectors.toList()); + } + + RelationOperateLogRoleBindResourceDO operateAfter = RelationOperateLogRoleBindResourceDO.builder() + .uniCodes(uniCodes) + .roleCode(saasRole.getRoleCode()) + .build(); + SaasPgroupPermissionRelationOperateLog operateLog = SaasPgroupPermissionRelationOperateLog.builder() + .tableName(SaasPgroupPermissionRelationOperateLogServiceImpl.TABLE_NAME_ROLE) + .scene(PermissionRelationOperateLogSceneEnum.OLD_OMS_ROLE_BIND_FEATURE.getValue()) + .sceneId(saasRole.getRoleCode()) + .createBy(saveOrUpdateRole.getOperatorId()) + .createByName(saveOrUpdateRole.getOperatorName()) + .traceId(MDC.get(Constants.CTX_LOG_ID_MDC)) + .requestData(Objects.isNull(saveOrUpdateRole) ? null : JSONObject.toJSONString(saveOrUpdateRole)) + .operateData(JSONObject.toJSONString(Lists.newArrayList(operateAfter))) + .createByRole(JSONObject.toJSONString(saasPgroupPermissionRelationOperateLogService.getPersonBasicRoles(saveOrUpdateRole.getOperatorId()))) + .build(); + + saasPgroupPermissionRelationOperateLogService.batchSave(Lists.newArrayList(operateLog)); + } + + /** + * + * @param saveOrUpdateRole + * @param resourcePermissions + * @param saasPermissionGroup + */ + private void saveSaasFeatureResourcePermission(SaveOrUpdateRoleVO saveOrUpdateRole, + List resourcePermissions, + SaasPermissionGroup saasPermissionGroup) { + if (CollectionUtils.isEmpty(saveOrUpdateRole.getPermissionIds())) { + return; + } + + Map resourcePermissionMap = resourcePermissions.stream() + .collect(Collectors.toMap(ResourcePermission::getId, Function.identity())); + + // 保存权限集和权限点映射关系 + List pgpRelations = saveOrUpdateRole.getPermissionIds().stream() + .map(ppId -> { + ResourcePermission resourcePermission = resourcePermissionMap.get(ppId); + SaasPgroupPermissionRelation target = new SaasPgroupPermissionRelation(); + target.setGroupId(saasPermissionGroup.getId()); + target.setFeatureId(ppId); + target.setCreateBy(saveOrUpdateRole.getOperatorId()); + target.setUpdateBy(saveOrUpdateRole.getOperatorId()); + target.setFeatureType(resourcePermission.getFeatureType()); + target.setType(NEW_FEATURE); + target.setTerminal(resourcePermission.getTerminal()); + return target; + }).collect(Collectors.toList()); + SaasPgroupPermissionRelationService.UpsertPermissionRelationParam upsertPermissionRelationParam = SaasPgroupPermissionRelationService.UpsertPermissionRelationParam.builder() + .groupIds(Lists.newArrayList(saasPermissionGroup.getId())) + .relations(pgpRelations) + .type(NEW_FEATURE) + .operatorId(saveOrUpdateRole.getOperatorId()) + .requestData(saveOrUpdateRole) + .build(); + saasPgroupPermissionRelationService.saveOrUpdate(upsertPermissionRelationParam); + + // 记录操作日志 + try { + saveOperateLog4RoleBindFeatureResource(saveOrUpdateRole); + } catch (Exception e) { + log.warn("save operate log error", e); } } @@ -620,9 +673,10 @@ public class RoleServiceImpl extends ServiceImpl return saveOrUpdateRoleVO.getProductUnitType(); } - private void validFeature(List featureIds) { + @Override + public List validFeature(List featureIds) { if (CollectionUtils.isEmpty(featureIds)) { - return; + return Collections.emptyList(); } List saasFeatures = saasFeatureDao.lambdaQuery().in(SaasFeature::getId, featureIds) .eq(SaasFeature::getIsDelete, TableIsDeleteEnum.NORMAL.value).list(); @@ -634,6 +688,7 @@ public class RoleServiceImpl extends ServiceImpl if (CollectionUtils.isNotEmpty(invalidFeatues)) { throw new ServiceException("权限点信息错误"); } + return saasFeatures; } private SaasPermissionGroup validPermissionGroupCommon(SaveOrUpdateRoleVO saveOrUpdateRole) { @@ -697,36 +752,6 @@ public class RoleServiceImpl extends ServiceImpl .list(); } - @Override - public Set filterPermissionPoint(Set role, TyrSaasAuthServiceImpl.OUWRoleInfo userRoleInfoMap) { - // 按钮级别权限点ID - Set result = new HashSet<>(); - if (CollectionUtils.isEmpty(role)) { - return result; - } - Set initRoleId = new HashSet<>(); - //有标准角,查询角色分组 - if (role.stream().anyMatch(r -> Objects.equals(RoleTypeEnum.INIT.getValue(), r.getRoleType()))) { - List roleGroupList = saasRoleGroupService.getList(QuerySaasRoleGroupReq.builder() - .workspaceTypeCode(Collections.singletonList(userRoleInfoMap.getWorkspaceType().toString())) - // .ouTypeCode(Collections.singletonList(userRoleInfoMap.getWorkspaceJoinType().getValue().toString())) - .build()); - initRoleId = roleGroupList.stream().map(SaasRoleGroupVO::getRoleIds).filter(CollectionUtil::isNotEmpty).flatMap(List::stream).collect(Collectors.toSet()); - } - - Set finalInitRoleId = initRoleId; - List resultRole = role.stream().filter(r -> - Objects.equals(r.getRoleType(), RoleTypeEnum.COMMON.getValue()) - || finalInitRoleId.contains(r.getId())) - .collect(Collectors.toList()); - - return resultRole.stream().map( - e -> e.getMatchFeature(userRoleInfoMap.workspaceId, userRoleInfoMap.getOuId())) - .flatMap(List::stream) - .map(PermissionPointTreeNode::getPermissionPointId) - .collect(Collectors.toSet()); - } - @Override public PageResp queryRoleWithUser(RoleWithUserQueryReq req) { page2Default(req); @@ -785,25 +810,6 @@ public class RoleServiceImpl extends ServiceImpl } } - @Override - public List queryRoleByRoleTypes(QueryByIdentityIdTypeReq req, List roleTypes) { - List roleIds = roleUserRelationDao.query(req.getIdentityId(), req.getIdentityType(), - req.getWorkspaceId(), req.getOuId()).stream().map(SaasRoleUserRelation::getRoleId).collect(Collectors.toList()); - List list = new ArrayList<>(); - if (CollectionUtils.isNotEmpty(roleIds)) { - list = saasRoleDao.lambdaQuery() - .in(BaseEntity::getId, roleIds) - .in(SaasRole::getRoleType, roleTypes) - .list(); - } - return BeanUtil.copyToList(list, SaasRoleVO.class); - } - - @Override - public List listForOUWorkspace(Long ouId, Long workspaceId, Integer workspaceJoinType) { - return saasRoleDao.listForOUWorkspace(ouId, workspaceId, workspaceJoinType); - } - @Override public List findRoleByName(QueryRoleByNameReq req) { // TODO jhy 这个方法有问题 @@ -898,11 +904,6 @@ public class RoleServiceImpl extends ServiceImpl saasPgroupRoleRelationDao.deleteByRoleId(deleteRoleParam.getRoleIds()); } - @Override - public List queryRoleByFeatures(Set matchedFeatureIds) { - return saasRoleDao.getBaseMapper().listRoleByFeatures(matchedFeatureIds); - } - @Override public List getByIds(Set ids) { return saasRoleDao.listByIds(ids); @@ -1164,28 +1165,40 @@ public class RoleServiceImpl extends ServiceImpl } @Override - public Page page(PageSaasRoleParam param) { + public cn.axzo.foundation.page.PageResp page(PageSaasRoleParam param) { QueryWrapper wrapper = QueryWrapperHelper.fromBean(param, SaasRole.class); wrapper.eq("is_delete", 0); - Page page = this.page(PageConverter.convertToMybatis(param, SaasRole.class), wrapper); + IPage page = this.page(PageConverter.toMybatis(param, SaasRole.class), wrapper); Map> saasRoleGroups = listRoleGroups(param, page.getRecords()); Map> saasPermissions = listRolePermissions(param, page.getRecords()); - return PageConverter.convert(page, (record) -> from(record, + Map> saasPermissionsOld = listRolePermissionsOld(param, page.getRecords()); + + Map> saasRoleUsers = listSaasRoleUser(param, page.getRecords()); + + Map> permissionRelations = listRolePermissionRelations(param, page.getRecords()); + + return PageConverter.toResp(page, (record) -> from(record, saasRoleGroups, - saasPermissions)); + BooleanUtils.isTrue(param.getNeedPermissionOld()) ? saasPermissionsOld : saasPermissions, + saasRoleUsers, + permissionRelations)); } - + private SaasRoleRes from(SaasRole saasRole, Map> saasRoleGroups, - Map> saasPermissions) { + Map> saasPermissions, + Map> saasRoleUsers, + Map> permissionRelations) { SaasRoleRes saasRoleRes = SaasRoleRes.builder().build(); BeanUtils.copyProperties(saasRole, saasRoleRes); saasRoleRes.setSaasRoleGroups(saasRoleGroups.get(saasRoleRes.getId())); saasRoleRes.setSaasPermissions(saasPermissions.get(saasRoleRes.getId())); + saasRoleRes.setSaasRoleUsers(saasRoleUsers.get(saasRoleRes.getId())); + saasRoleRes.setPermissionRelations(permissionRelations.get(saasRoleRes.getId())); return saasRoleRes; } @@ -1204,6 +1217,8 @@ public class RoleServiceImpl extends ServiceImpl List saasPgroupPermissionRelations = saasPgroupPermissionRelationDao.lambdaQuery() .in(SaasPgroupPermissionRelation::getGroupId, Lists.transform(saasPgroupRoleRelations, SaasPgroupRoleRelation::getGroupId)) .eq(SaasPgroupPermissionRelation::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .in(CollectionUtils.isNotEmpty(param.getFeatureIds()), SaasPgroupPermissionRelation::getFeatureId, param.getFeatureIds()) + .eq(SaasPgroupPermissionRelation::getType, NEW_FEATURE) .list(); if (CollectionUtils.isEmpty(saasPgroupPermissionRelations)) { return Collections.emptyMap(); @@ -1317,6 +1332,12 @@ public class RoleServiceImpl extends ServiceImpl saasFeatureResourceService.updateFeatureAuthType(item.getFeatureId(), item.getAuthType()); if (CollectionUtil.isEmpty(item.getRoleIds()) || item.getAuthType() == 0) { saasPgroupPermissionRelationDao.removeByPermissionPointIds(Collections.singletonList(item.getFeatureId())); + // 记录操作日志 + try { + saveOperateLog(item.getFeatureId(), operatorId, req); + } catch (Exception e) { + log.warn("save operate log error", e); + } } else { List existGroupIds = saasPgroupPermissionRelationDao.queryByFeatureIds(Collections.singletonList(item.getFeatureId())) .stream().map(SaasPgroupPermissionRelation::getGroupId).collect(Collectors.toList()); @@ -1325,7 +1346,12 @@ public class RoleServiceImpl extends ServiceImpl .stream().map(SaasPgroupRoleRelation::getGroupId).collect(Collectors.toList()); List insertGroupIds = groupIds.stream().filter(role -> !existGroupIds.contains(role)).collect(Collectors.toList()); List deleteGroupIds = existGroupIds.stream().filter(role -> !groupIds.contains(role)).collect(Collectors.toList()); - + // 记录操作日志 + try { + saveOperateLog(item.getFeatureId(), operatorId, item.getRoleIds(), req); + } catch (Exception e) { + log.warn("save operate log error", e); + } // 新增的 if (CollectionUtils.isNotEmpty(insertGroupIds)) { List insertRelation = new ArrayList<>(); @@ -1334,6 +1360,9 @@ public class RoleServiceImpl extends ServiceImpl relation.setFeatureId(item.getFeatureId()); relation.setGroupId(groupId); relation.setCreateBy(operatorId); + relation.setFeatureType(item.getFeatureType()); + relation.setType(NEW_FEATURE); + relation.setTerminal(item.getTerminal()); insertRelation.add(relation); }); saasPgroupPermissionRelationDao.saveBatch(insertRelation); @@ -1366,9 +1395,9 @@ public class RoleServiceImpl extends ServiceImpl return resp; } - private void validPermission(Set permissionIds) { + private List validPermission(Set permissionIds) { if (CollectionUtils.isEmpty(permissionIds)) { - return; + return Collections.emptyList(); } List resourcePermissions = saasFeatureResourceService.permissionQuery(ResourcePermissionQueryDTO.builder() .ids(Lists.newArrayList(permissionIds)) @@ -1377,5 +1406,256 @@ public class RoleServiceImpl extends ServiceImpl permissionIds.removeAll(resourcePermissions.stream().map(ResourcePermission::getId).collect(Collectors.toSet())); throw new ServiceException(String.format("权限点 %s 信息错误", permissionIds)); } + return resourcePermissions; + } + + private Map> listSaasRoleUser(PageSaasRoleParam param, + List saasRoles) { + if (CollectionUtils.isEmpty(saasRoles) || BooleanUtils.isNotTrue(param.getNeedRoleUser())) { + return Collections.emptyMap(); + } + + List roleIds = Lists.transform(saasRoles, SaasRole::getId); + + ListRoleUserRelationParam listRoleUserRelationParam = ListRoleUserRelationParam.builder() + .roleIds(roleIds) + .needUsers(param.getNeedRoleUser()) + .build(); + List saasRoleUserV2DTOS = saasRoleUserRelationService.listV2(listRoleUserRelationParam); + + return saasRoleUserV2DTOS.stream() + .map(e -> Pair.of(e.getRoleId(), e.getSaasRoleUser())) + .collect(Collectors.groupingBy(Pair::getKey, Collectors.mapping(Pair::getValue, Collectors.toList()))); + } + + /** + * 待所有端的权限点都迁移到saas_feature_resource后就删除 + * @param param + * @param saasRoles + * @return + */ + private Map> listRolePermissionsOld(PageSaasRoleParam param, + List saasRoles) { + if (CollectionUtils.isEmpty(saasRoles) || BooleanUtils.isNotTrue(param.getNeedPermissionOld())) { + return Collections.emptyMap(); + } + + List saasPgroupRoleRelations = saasPgroupRoleRelationDao.findByRoleIds(Lists.transform(saasRoles, SaasRole::getId)); + + if (CollectionUtils.isEmpty(saasPgroupRoleRelations)) { + return Collections.emptyMap(); + } + + List saasPgroupPermissionRelations = saasPgroupPermissionRelationDao.lambdaQuery() + .in(SaasPgroupPermissionRelation::getGroupId, Lists.transform(saasPgroupRoleRelations, SaasPgroupRoleRelation::getGroupId)) + .in(CollectionUtils.isNotEmpty(param.getFeatureIds()), SaasPgroupPermissionRelation::getFeatureId, param.getFeatureIds()) + .eq(SaasPgroupPermissionRelation::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .eq(SaasPgroupPermissionRelation::getType, OLD_FEATURE) + .list(); + if (CollectionUtils.isEmpty(saasPgroupPermissionRelations)) { + return Collections.emptyMap(); + } + + List featureIds = Lists.transform(saasPgroupPermissionRelations, SaasPgroupPermissionRelation::getFeatureId); + + Map resourcePermissions = saasFeatureDao.listByIds(featureIds).stream() + .map(e -> SaasPermissionRes.builder() + .id(e.getId()) + .featureCode(e.getFeatureCode()) + .terminal(e.getTerminal()) + .build()) + .collect(Collectors.toMap(SaasPermissionRes::getId, Function.identity())); + + Map> pgroupPermissions = saasPgroupPermissionRelations.stream() + .collect(Collectors.groupingBy(SaasPgroupPermissionRelation::getGroupId, + Collectors.mapping(SaasPgroupPermissionRelation::getFeatureId, Collectors.toList()))); + + return saasPgroupRoleRelations.stream() + .map(e -> { + List permissionIds = pgroupPermissions.get(e.getGroupId()); + if (CollectionUtils.isEmpty(permissionIds)) { + return null; + } + + return permissionIds.stream() + .map(permissionId -> { + SaasPermissionRes saasPermissionRes = resourcePermissions.get(permissionId); + return SaasPermissionWrapper.from(e, saasPermissionRes); + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + }) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .collect(Collectors.groupingBy(SaasPermissionWrapper::getRoleId, + Collectors.mapping(e -> SaasPermissionRes.builder().id(e.getId()).featureCode(e.getFeatureCode()).build(), + Collectors.toList()))); + } + + private Map> listRolePermissionRelations(PageSaasRoleParam param, + List saasRoles) { + if (CollectionUtils.isEmpty(saasRoles) || BooleanUtils.isNotTrue(param.getNeedPermissionRelation())) { + return Collections.emptyMap(); + } + + List saasPgroupRoleRelations = saasPgroupRoleRelationDao.findByRoleIds(Lists.transform(saasRoles, SaasRole::getId)); + + if (CollectionUtils.isEmpty(saasPgroupRoleRelations)) { + return Collections.emptyMap(); + } + + PagePgroupPermissionRelationReq pagePgroupPermissionRelationReq = PagePgroupPermissionRelationReq.builder() + .groupIds(Lists.transform(saasPgroupRoleRelations, SaasPgroupRoleRelation::getGroupId)) + .featureIds(param.getFeatureIds()) + .featureResourceTypes(param.getFeatureResourceTypes()) + .type(param.getType()) + .terminal(param.getTerminal()) + .build(); + List saasPgroupPermissionRelations = saasPgroupPermissionRelationService.list(pagePgroupPermissionRelationReq); + + Map> pgroupPermissions = saasPgroupPermissionRelations.stream() + .collect(Collectors.groupingBy(SaasPgroupPermissionRelation::getGroupId)); + + return saasPgroupRoleRelations.stream() + .map(e -> { + List permissionRelations = pgroupPermissions.get(e.getGroupId()); + if (CollectionUtils.isEmpty(permissionRelations)) { + return null; + } + + return permissionRelations.stream() + .map(permissionRelation -> SaasPermissionRelationWrapper.from(e, permissionRelation)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + }) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .collect(Collectors.groupingBy(SaasPermissionRelationWrapper::getRoleId, + Collectors.mapping(e -> SaasPermissionRelationRes.builder() + .featureId(e.getFeatureId()) + .type(e.getType()) + .featureType(e.getFeatureType()) + .build(), + Collectors.toList()))); + } + + private void saveOperateLog(Long featureId, Long operatorId, List req) { + SaasFeatureResource saasFeatureResource = saasFeatureResourceService.featureResourceById(featureId); + if (Objects.isNull(saasFeatureResource)) { + return; + } + List personProfileDtos = RpcInternalUtil.rpcListProcessor(() -> userProfileServiceApi.getPersonProfiles(Lists.newArrayList(operatorId)), "查询用户信息", operatorId).getData(); + PersonProfileDto operator = org.apache.commons.collections4.CollectionUtils.isEmpty(personProfileDtos) ? null : personProfileDtos.get(0); + + RelationOperateLogResourceBindRoleDO operateAfter = RelationOperateLogResourceBindRoleDO.builder() + .uniCode(saasFeatureResource.getUniCode()) + .build(); + SaasPgroupPermissionRelationOperateLog operateLog = SaasPgroupPermissionRelationOperateLog.builder() + .tableName(SaasPgroupPermissionRelationOperateLogServiceImpl.TABLE_NAME_FEATURE_RESOURCE) + .scene(PermissionRelationOperateLogSceneEnum.OMS_FEATURE_RESOURCE_BIND_ROLE.getValue()) + .sceneId(saasFeatureResource.getUniCode()) + .createBy(operatorId) + .createByName(Objects.isNull(operator) ? "" : operator.getRealName()) + .traceId(MDC.get(Constants.CTX_LOG_ID_MDC)) + .requestData(Objects.isNull(req) ? null : JSONObject.toJSONString(req)) + .operateData(JSONObject.toJSONString(Lists.newArrayList(operateAfter))) + .createByRole(JSONObject.toJSONString(saasPgroupPermissionRelationOperateLogService.getPersonBasicRoles(operatorId))) + .build(); + + saasPgroupPermissionRelationOperateLogService.batchSave(Lists.newArrayList(operateLog)); + } + + private void saveOperateLog(Long featureId, Long operatorId, List roleIds, List req) { + SaasFeatureResource saasFeatureResource = saasFeatureResourceService.featureResourceById(featureId); + if (Objects.isNull(saasFeatureResource)) { + return; + } + List personProfileDtos = RpcInternalUtil.rpcListProcessor(() -> userProfileServiceApi.getPersonProfiles(Lists.newArrayList(operatorId)), "查询用户信息", operatorId).getData(); + PersonProfileDto operator = org.apache.commons.collections4.CollectionUtils.isEmpty(personProfileDtos) ? null : personProfileDtos.get(0); + List saasRoles = saasRoleDao.lambdaQuery().in(BaseEntity::getId, roleIds).list(); + + RelationOperateLogResourceBindRoleDO operateAfter = RelationOperateLogResourceBindRoleDO.builder() + .uniCode(saasFeatureResource.getUniCode()) + .roleCodes(saasRoles.stream().map(SaasRole::getRoleCode).collect(Collectors.toList())) + .build(); + SaasPgroupPermissionRelationOperateLog operateLog = SaasPgroupPermissionRelationOperateLog.builder() + .tableName(SaasPgroupPermissionRelationOperateLogServiceImpl.TABLE_NAME_FEATURE_RESOURCE) + .scene(PermissionRelationOperateLogSceneEnum.OMS_FEATURE_RESOURCE_BIND_ROLE.getValue()) + .sceneId(saasFeatureResource.getUniCode()) + .createBy(operatorId) + .createByName(Objects.isNull(operator) ? "" : operator.getRealName()) + .traceId(MDC.get(Constants.CTX_LOG_ID_MDC)) + .requestData(Objects.isNull(req) ? null : JSONObject.toJSONString(req)) + .operateData(JSONObject.toJSONString(Lists.newArrayList(operateAfter))) + .createByRole(JSONObject.toJSONString(saasPgroupPermissionRelationOperateLogService.getPersonBasicRoles(operatorId))) + .build(); + + saasPgroupPermissionRelationOperateLogService.batchSave(Lists.newArrayList(operateLog)); + } + + private void saveOperateLog4RoleBindFeatureResource(SaveOrUpdateRoleVO saveOrUpdateRole) { + SaasRole saasRole = saasRoleDao.getById(saveOrUpdateRole.getId()); + List uniCodes = null; + if (CollectionUtils.isNotEmpty(saveOrUpdateRole.getPermissionIds())) { + uniCodes = saasFeatureResourceDao.lambdaQuery().in(BaseEntity::getId, saveOrUpdateRole.getPermissionIds()).list().stream().map(SaasFeatureResource::getUniCode).collect(Collectors.toList()); + } + + RelationOperateLogRoleBindResourceDO operateAfter = RelationOperateLogRoleBindResourceDO.builder() + .uniCodes(uniCodes) + .roleCode(saasRole.getRoleCode()) + .build(); + SaasPgroupPermissionRelationOperateLog operateLog = SaasPgroupPermissionRelationOperateLog.builder() + .tableName(SaasPgroupPermissionRelationOperateLogServiceImpl.TABLE_NAME_ROLE) + .scene(PermissionRelationOperateLogSceneEnum.OMS_ROLE_BIND_FEATURE_RESOURCE.getValue()) + .sceneId(saasRole.getRoleCode()) + .createBy(saveOrUpdateRole.getOperatorId()) + .createByName(saveOrUpdateRole.getOperatorName()) + .traceId(MDC.get(Constants.CTX_LOG_ID_MDC)) + .requestData(Objects.isNull(saveOrUpdateRole) ? null : JSONObject.toJSONString(saveOrUpdateRole)) + .operateData(JSONObject.toJSONString(Lists.newArrayList(operateAfter))) + .createByRole(JSONObject.toJSONString(saasPgroupPermissionRelationOperateLogService.getPersonBasicRoles(saveOrUpdateRole.getOperatorId()))) + .build(); + + saasPgroupPermissionRelationOperateLogService.batchSave(Lists.newArrayList(operateLog)); + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + static class SaasPermissionRelationWrapper { + + /** + * 角色id + */ + private Long roleId; + + /** + * 菜单资源树节点id + */ + private Long featureId; + + /** + * 新旧菜单资源数标识 + */ + private Integer type; + + /** + * 菜单资源树节点类型 + */ + private Integer featureType; + + public static SaasPermissionRelationWrapper from(SaasPgroupRoleRelation saasPgroupRoleRelation, + SaasPgroupPermissionRelation permissionRelation) { + if (permissionRelation == null) { + return null; + } + return SaasPermissionRelationWrapper.builder() + .roleId(saasPgroupRoleRelation.getRoleId()) + .featureId(permissionRelation.getFeatureId()) + .type(permissionRelation.getType()) + .featureType(permissionRelation.getFeatureType()) + .build(); + } } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RoleUserService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RoleUserService.java index 4d2bec03..7fb3dd6e 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RoleUserService.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/RoleUserService.java @@ -1,5 +1,6 @@ package cn.axzo.tyr.server.service.impl; +import cn.axzo.basics.common.BeanMapper; import cn.axzo.basics.common.constant.enums.TableIsDeleteEnum; import cn.axzo.basics.common.exception.ServiceException; import cn.axzo.basics.common.util.AssertUtil; @@ -11,13 +12,16 @@ import cn.axzo.tyr.client.model.BaseWorkspaceModel; import cn.axzo.tyr.client.model.enums.DictWorkSpaceTypeEnum; import cn.axzo.tyr.client.model.enums.IdentityType; import cn.axzo.tyr.client.model.enums.WorkerLeaderRoleEnum; +import cn.axzo.tyr.client.model.roleuser.RoleUserUpdateReq; import cn.axzo.tyr.client.model.roleuser.dto.GetUserAutoOwnRoleResp; +import cn.axzo.tyr.client.model.roleuser.dto.GetUserFeatureResourceIdsResp; import cn.axzo.tyr.client.model.roleuser.dto.IdentityInfo; import cn.axzo.tyr.client.model.roleuser.dto.SuperAminInfoResp; import cn.axzo.tyr.client.model.roleuser.req.AutoOwnRoleUserReq; import cn.axzo.tyr.client.model.roleuser.req.CreateSuperAdminRoleParam; import cn.axzo.tyr.client.model.roleuser.req.GantOrUnGantaWorkerLeaderRoleReq; import cn.axzo.tyr.client.model.roleuser.req.GetUserAutoOwnRoleReq; +import cn.axzo.tyr.client.model.roleuser.req.GetUserFeatureResourceIdsReq; import cn.axzo.tyr.client.model.roleuser.req.RoleUserReq; import cn.axzo.tyr.client.model.roleuser.req.SuperAdminParam; import cn.axzo.tyr.client.model.roleuser.req.WorkerManagerRoleUserReq; @@ -183,6 +187,8 @@ public class RoleUserService implements SaasRoleUserService { saasRoleUserRelation.setNaturalPersonId(req.getPersonId()); saasRoleUserRelation.setOuId(req.getOuId()); saasRoleUserRelation.setWorkspaceId(req.getWorkspaceId()); + saasRoleUserRelation.setCreateBy(req.getOperator()); + saasRoleUserRelation.setUpdateBy(req.getOperator()); return saasRoleUserRelation; }).collect(Collectors.toList())); @@ -253,18 +259,18 @@ public class RoleUserService implements SaasRoleUserService { private SaasRole findSuperAdmin(Long workspaceId, Long ouId, Integer workspaceType) { //优先取租户超管 没有再取标准角色超管 //租户超管 - SaasRole superAdmin = saasRoleDao + return saasRoleDao .findRoleByTypeAndWorkspaceIdAndOuId(RoleTypeEnum.SUPER_ADMIN.getValue(), workspaceId, ouId); - if (superAdmin != null) { - return superAdmin; - } +// if (superAdmin != null) { +// return superAdmin; +// } //标准角超管 - String superAdminCode = superAdminCodes.get(workspaceType); - if (StrUtil.isBlank(superAdminCode)) { - throw new ServiceException("租户类型[" + workspaceType + "]未配置超管编码"); - } - return saasRoleDao.lambdaQuery().eq(SaasRole::getRoleCode, superAdminCode).one(); +// String superAdminCode = superAdminCodes.get(workspaceType); +// if (StrUtil.isBlank(superAdminCode)) { +// throw new ServiceException("租户类型[" + workspaceType + "]未配置超管编码"); +// } +// return saasRoleDao.lambdaQuery().eq(SaasRole::getRoleCode, superAdminCode).one(); } private void checkRoleName(String name, Long workspaceId, Long ouId) { @@ -574,6 +580,32 @@ public class RoleUserService implements SaasRoleUserService { .build(); } + @Override + public GetUserFeatureResourceIdsResp getUserFeatureResourceIds(GetUserFeatureResourceIdsReq req) { + // 查询用户所有角色 + List existsRoleUser = roleUserRelationDao.queryByPersonId(req.getPersonId(), req.getWorkspaceId(), req.getOuId()); + if (CollectionUtils.isEmpty(existsRoleUser)) { + return null; + } + List roles = saasRoleDao.listByIds(existsRoleUser.stream().mapToLong(SaasRoleUserRelation::getRoleId).boxed().collect(Collectors.toList())) + .stream().filter(e -> CollectionUtils.isEmpty(req.getRoleIds()) || req.getRoleIds().contains(e.getId())).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(roles)) { + return null; + } + + List saasPgroupRoleRelations = saasPgroupRoleRelationDao.findByRoleIds(roles.stream().map(BaseEntity::getId).collect(Collectors.toSet())); + if (CollectionUtils.isEmpty(saasPgroupRoleRelations)) { + return null; + } + + List saasPgroupPermissionRelations = saasPgroupPermissionRelationDao.lambdaQuery() + .in(SaasPgroupPermissionRelation::getGroupId, saasPgroupRoleRelations.stream().map(SaasPgroupRoleRelation::getGroupId).collect(Collectors.toList())) + .eq(SaasPgroupPermissionRelation::getIsDelete, TableIsDeleteEnum.NORMAL.value).list(); + + return GetUserFeatureResourceIdsResp.builder().featureResourceIds(saasPgroupPermissionRelations.stream() + .map(SaasPgroupPermissionRelation::getFeatureId).collect(Collectors.toSet())).build(); + } + private void batchRemoveByRoleUserInfo(RoleUserInfo roleUserBaseInfo) { roleUserBaseInfo.getRoleUserResourceInfos().forEach(e -> { @@ -610,4 +642,13 @@ public class RoleUserService implements SaasRoleUserService { } return existsPgroupPermissionRelations.stream().filter(e -> !featureIds.contains(e.getFeatureId())).map(SaasPgroupPermissionRelation::getId).collect(Collectors.toSet()); } + + @Override + public Boolean batchUpdateById(List roleUserUpdateReqs) { + if (CollectionUtils.isEmpty(roleUserUpdateReqs)) { + return false; + } + List saasRoleUserDTOList = BeanMapper.copyList(roleUserUpdateReqs, SaasRoleUserRelation.class); + return roleUserRelationDao.updateBatchById(saasRoleUserDTOList); + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasFeatureResourceServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasFeatureResourceServiceImpl.java index a52f3449..eee7e6d4 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasFeatureResourceServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasFeatureResourceServiceImpl.java @@ -3,39 +3,54 @@ package cn.axzo.tyr.server.service.impl; import cn.axzo.basics.common.BeanMapper; import cn.axzo.basics.common.util.StopWatchUtil; import cn.axzo.basics.common.util.TreeUtil; -import cn.axzo.framework.domain.web.BizException; +import cn.axzo.foundation.dao.support.converter.PageConverter; +import cn.axzo.foundation.dao.support.mysql.QueryWrapperHelper; +import cn.axzo.foundation.exception.Axssert; +import cn.axzo.foundation.page.PageResp; import cn.axzo.framework.domain.web.code.BaseCode; import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; -import cn.axzo.pokonyan.config.redis.RedisClient; import cn.axzo.tyr.client.common.enums.FeatureResourceAuthType; import cn.axzo.tyr.client.common.enums.FeatureResourceStatus; import cn.axzo.tyr.client.common.enums.FeatureResourceType; -import cn.axzo.tyr.client.model.req.CommonDictQueryReq; +import cn.axzo.tyr.client.common.enums.PageElementFeatureResourceRelationTypeEnum; +import cn.axzo.tyr.client.model.req.DeleteFeatureResourceReq; import cn.axzo.tyr.client.model.req.FeatureComponentSaveReq; import cn.axzo.tyr.client.model.req.FeatureResourceTreeSaveReq; import cn.axzo.tyr.client.model.req.GetFeatureResourceTreeReq; -import cn.axzo.tyr.client.model.res.CommonDictResp; +import cn.axzo.tyr.client.model.req.ModifyPageElementRelationDTO; +import cn.axzo.tyr.client.model.req.PageElementFeatureResourceRelationReq; +import cn.axzo.tyr.client.model.req.PagePgroupPermissionRelationReq; +import cn.axzo.tyr.client.model.req.PageSaasFeatureResourceReq; import cn.axzo.tyr.client.model.res.FeatureResourceDTO; import cn.axzo.tyr.client.model.res.FeatureResourceTreeNode; +import cn.axzo.tyr.client.model.res.PageElementBasicDTO; +import cn.axzo.tyr.client.model.res.SaasFeatureResourceResp; import cn.axzo.tyr.server.common.util.Throws; import cn.axzo.tyr.server.model.ResourcePermission; import cn.axzo.tyr.server.model.ResourcePermissionQueryDTO; import cn.axzo.tyr.server.model.convert.SaasFeatureResourceConvert; import cn.axzo.tyr.server.repository.dao.SaasFeatureResourceDao; import cn.axzo.tyr.server.repository.entity.SaasFeatureResource; -import cn.axzo.tyr.server.service.RoleService; -import cn.axzo.tyr.server.service.SaasCommonDictService; +import cn.axzo.tyr.server.repository.entity.SaasPageElementFeatureResourceRelation; +import cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation; +import cn.axzo.tyr.server.repository.mapper.SaasFeatureResourceMapper; import cn.axzo.tyr.server.service.SaasFeatureResourceService; +import cn.axzo.tyr.server.service.SaasPageElementFeatureResourceRelationService; +import cn.axzo.tyr.server.service.SaasPageElementService; import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationService; import cn.azxo.framework.common.utils.StringUtils; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.google.common.collect.Lists; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.springframework.beans.BeanUtils; import org.springframework.cache.annotation.CacheEvict; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -46,14 +61,11 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Function; import java.util.stream.Collectors; -import static cn.axzo.framework.domain.web.code.BaseCode.BAD_REQUEST; -import static cn.axzo.framework.domain.web.code.BaseCode.SERVER_ERROR; -import static cn.axzo.tyr.server.common.constants.CacheConstant.KEY_AUTH_FREE; +import static cn.axzo.tyr.server.config.exception.BizResultCode.FEATURE_RESOURCE_NOT_FOUND; +import static cn.axzo.tyr.server.repository.entity.SaasFeatureResource.DEFAULT_WORKSPACE_TYPE; +import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.NEW_FEATURE; /** * 功能资源服务实现 @@ -66,16 +78,16 @@ import static cn.axzo.tyr.server.common.constants.CacheConstant.KEY_AUTH_FREE; @Slf4j @Service @RequiredArgsConstructor -public class SaasFeatureResourceServiceImpl implements SaasFeatureResourceService { +public class SaasFeatureResourceServiceImpl extends ServiceImpl + implements SaasFeatureResourceService { - /** 功能资源树根节点所有端配置的scope **/ - private static final String RESOURCE_TERMINAL_SCOPE = "resource.terminal"; private final SaasFeatureResourceDao featureResourceDao; private final SaasFeatureResourceCacheService saasFeatureResourceCacheService; - private final SaasCommonDictService saasCommonDictService; - private final SaasPgroupPermissionRelationService pgroupPermissionRelationService; + private final SaasPageElementService saasPageElementService; + private final SaasPgroupPermissionRelationService saasPgroupPermissionRelationService; + private final SaasPageElementFeatureResourceRelationService saasPageElementFeatureResourceRelationService; @Override public List listNavByIds(List featureIds, List featureTypes) { @@ -103,7 +115,8 @@ public class SaasFeatureResourceServiceImpl implements SaasFeatureResourceServic SaasFeatureResource::getParentId, SaasFeatureResource::getFeatureCode, SaasFeatureResource::getFeatureType, - SaasFeatureResource::getAuthType) + SaasFeatureResource::getAuthType, + SaasFeatureResource::getTerminal) .in(CollectionUtil.isNotEmpty(param.getIds()), SaasFeatureResource::getId, param.getIds()) .in(CollectionUtil.isNotEmpty(param.getFeatureTypes()), SaasFeatureResource::getFeatureType, param.getFeatureTypes()) .in(CollectionUtil.isNotEmpty(param.getFeatureCodes()), SaasFeatureResource::getFeatureCode, param.getFeatureCodes()) @@ -121,17 +134,6 @@ public class SaasFeatureResourceServiceImpl implements SaasFeatureResourceServic @Override public List getTree(GetFeatureResourceTreeReq req) { - List commonDictResponses = saasCommonDictService.query(CommonDictQueryReq.builder().scope(RESOURCE_TERMINAL_SCOPE).build()); - if (CollectionUtils.isEmpty(commonDictResponses)) { - log.error("not config resource terminal in saas_common_dict table"); - return Collections.emptyList(); - } - - final AtomicLong rootIdIndex = new AtomicLong(0L); - List rootNodes = commonDictResponses.stream().map(e -> FeatureResourceTreeNode.builder() - .id(rootIdIndex.decrementAndGet()).terminal(e.getDictCode()).featureName(e.getDictValue()) - .featureCode(e.getDictCode()).featureType(-1).children(Lists.newArrayList()).build()).collect(Collectors.toList()); - StopWatchUtil watch = StopWatchUtil.createStarted("feature-resource-tree"); watch.start("dbQuery"); List saasFeatureResources = saasFeatureResourceCacheService.getByResourceTreeParam(req); @@ -141,25 +143,43 @@ public class SaasFeatureResourceServiceImpl implements SaasFeatureResourceServic } List treeList = TreeUtil.buildTree(saasFeatureResources.stream() + // 因为非OMS端可能会根据应用范围来查询菜单资源,现在只有分组和页面有配置应用范围 + // 非分组和页面的菜单资源应该都能看到,所以只有有配置应用范围的菜单资源才进行过滤 + .filter(e -> filterWorkspaceType(req, e)) .map(this::featureResource2Node) .sorted(Comparator.comparing(FeatureResourceDTO::getDisplayOrder)) - .collect(Collectors.toList())); - saasFeatureResources = null; // help GC + .collect(Collectors.toList())) + .stream() + // 因为存在tree中某个节点被隐藏,导致它的子节点构建在tree的root上,应该排除掉,root节点一定是parentId == 0的 + .filter(e -> e.getParentId() == 0) + .collect(Collectors.toList()); //搜索或需要按授权策略过滤 - 有额外的过滤条件 watch.start("filter-node"); - List filterResultNode = filterTreeNode(req.getKeyword(), treeList); + List resultNode = filterTreeNode(req.getKeyword(), treeList); watch.stop(); - //把处理后的树结构添加上根节点 - watch.start("fill-children-to-root"); - List resultNode = fillChildren2Root(rootNodes, filterResultNode); - watch.stop(); + if (Boolean.TRUE.equals(req.getNeedFilterBlankMenu())) { + resultNode = filterBlankMenu(resultNode); + } log.info("feature-resource-tree cost:{} , ms:{}", watch.prettyPrint(), watch.getTotalTimeMillis()); return resultNode; } + private boolean filterWorkspaceType(GetFeatureResourceTreeReq req, SaasFeatureResource e) { + if (Objects.isNull(req.getWorkspaceType())) { + return true; + } + + // 数据库默认值为0 + if (Objects.equals(e.getWorkspaceType(), DEFAULT_WORKSPACE_TYPE)) { + return true; + } + + return Objects.equals(e.getWorkspaceType(), req.getWorkspaceType()); + } + @Override public FeatureResourceTreeNode getTreeFeatureDescendant(Long featureId, Integer featureType) { List descendants = featureResourceDao.lambdaQuery() @@ -174,7 +194,11 @@ public class SaasFeatureResourceServiceImpl implements SaasFeatureResourceServic .map(SaasFeatureResourceConvert.INSTANCE::convert) .sorted(Comparator.comparing(FeatureResourceDTO::getDisplayOrder)) .collect(Collectors.toList())); - return treeList.get(0); + FeatureResourceTreeNode featureResourceTreeNode = treeList.get(0); + + List allUniCodes = descendants.stream().map(SaasFeatureResource::getUniCode).filter(StringUtils::isNotBlank).collect(Collectors.toList()); + fillPageElement2PageFeatureResource(treeList, allUniCodes); + return featureResourceTreeNode; } @Override @@ -195,13 +219,25 @@ public class SaasFeatureResourceServiceImpl implements SaasFeatureResourceServic SaasFeatureResource maxOrderResource = parallelResources.stream().max(Comparator.comparingInt(SaasFeatureResource::getDisplayOrder)).orElse(new SaasFeatureResource()); baseResource.setDisplayOrder(maxOrderResource.getDisplayOrder() + 1); } + // 生成唯一编码,用于pre菜单同步 + baseResource.setFeatureCode(req.getUniCode()); newResource(baseResource, parent == null ? "" : parent.getPath()); } else { //补充path SaasFeatureResource dbResource = featureResourceDao.getById(req.getId()); baseResource.setPath(dbResource.getPath()); + baseResource.setFeatureCode(req.getUniCode()); featureResourceDao.updateById(baseResource); } + // 保存组件与页面元素关系,如果是页面,改的是默认路由 + ModifyPageElementRelationDTO modifyPageElementRelation = ModifyPageElementRelationDTO.builder() + .operatorId(req.getOperatorId()) + .terminal(req.getTerminal()) + .featureResourceUniCode(req.getUniCode()) + .relationType(FeatureResourceType.PAGE.getCode().equals(req.getFeatureType()) ? PageElementFeatureResourceRelationTypeEnum.PAGE_ROUTE.getValue() : PageElementFeatureResourceRelationTypeEnum.DEFAULT.getValue()) + .pageElementCodes(req.getPageElementCodes()) + .build(); + saasPageElementService.modifyPageElementRelation(modifyPageElementRelation); if (CollectionUtil.isNotEmpty(req.getComponentSaveReqList())) { List components = req.getComponentSaveReqList(); @@ -235,7 +271,8 @@ public class SaasFeatureResourceServiceImpl implements SaasFeatureResourceServic private void newResource(SaasFeatureResource resource, String parentPath) { featureResourceDao.save(resource); //path追加自身ID - resource.setPath(StringUtils.isBlank(parentPath) ? resource.getId().toString() : parentPath + "," + resource.getId()); + // path增加","后缀,方便后续查询所有子节点 + resource.setPath(StringUtils.isBlank(parentPath) ? resource.getId().toString() + "," : parentPath + resource.getId() + ","); featureResourceDao.updateById(resource); } @@ -250,11 +287,20 @@ public class SaasFeatureResourceServiceImpl implements SaasFeatureResourceServic if (saasFeatureResource.getId() != null) { SaasFeatureResource dbResource = featureResourceDao.getById(saasFeatureResource.getId()); saasFeatureResource.setPath(dbResource.getPath()); + saasFeatureResource.setFeatureCode(featureComponentSaveReq.getUniCode()); featureResourceDao.updateById(saasFeatureResource); } else { saasFeatureResource.setCreateBy(operatorId); + saasFeatureResource.setFeatureCode(featureComponentSaveReq.getUniCode()); newResource(saasFeatureResource, parentPath); } + // 保存组件与页面元素关系 + saasPageElementService.modifyPageElementRelation(ModifyPageElementRelationDTO.builder() + .operatorId(operatorId) + .terminal(featureComponentSaveReq.getTerminal()) + .featureResourceUniCode(featureComponentSaveReq.getUniCode()) + .pageElementCodes(featureComponentSaveReq.getPageElementCodes()) + .build()); List children = featureComponentSaveReq.getChildren(); // 删除前端没带但数据库中有的组件。 @@ -285,31 +331,42 @@ public class SaasFeatureResourceServiceImpl implements SaasFeatureResourceServic .eq(SaasFeatureResource::getParentId, resourceId) .list(); List updateChildIds = submitChildren.stream().map(FeatureComponentSaveReq::getId).filter(Objects::nonNull).collect(Collectors.toList()); - List updateChildCodes = submitChildren.stream().map(FeatureComponentSaveReq::getFeatureCode).collect(Collectors.toList()); + List updateChildCodes = submitChildren.stream().map(FeatureComponentSaveReq::getUniCode).collect(Collectors.toList()); List deleteChildren = existChild.stream() .filter(item -> (!updateChildIds.contains(item.getId()) && !updateChildCodes.contains(item.getFeatureCode()))) .collect(Collectors.toList()); // 前端没传但是数据库中有的,需要删除 if (!CollectionUtil.isEmpty(deleteChildren)) { for (SaasFeatureResource deleteChild : deleteChildren) { - // 删除自己及自己的子集组件 - featureResourceDao.lambdaUpdate() + // 查询数据库要删除的组件 + List deletedResources = featureResourceDao.lambdaQuery() .eq(BaseEntity::getIsDelete,0) .eq(SaasFeatureResource::getFeatureType, FeatureResourceType.COMPONENT.getCode()) .apply("FIND_IN_SET('" + deleteChild.getId() + "', path)") - .set(SaasFeatureResource::getUpdateBy, operatorId) - .set(BaseEntity::getIsDelete,1) - .update(); + .list(); + if (!CollectionUtil.isEmpty(deletedResources)) { + // 删除自己及自己的子集组件 + featureResourceDao.lambdaUpdate() + .in(BaseEntity::getId, deletedResources.stream().map(BaseEntity::getId).collect(Collectors.toList())) + .set(SaasFeatureResource::getUpdateBy, operatorId) + .set(BaseEntity::getIsDelete,1) + .update(); + // 删除组件与页面元素的绑定关系 + saasPageElementService.deleteRelationByTerminalAndUniCodes(deleteChild.getTerminal(), deletedResources.stream().map(SaasFeatureResource::getUniCode).collect(Collectors.toList()), operatorId); + } } } } // 查询resource节点及子节点 @Override - public List listDescendant(Long featureId) { + public List batchListDescendant(List featureIds) { + if (CollectionUtils.isEmpty(featureIds)) { + return Collections.emptyList(); + } return featureResourceDao.lambdaQuery() .eq(BaseEntity::getIsDelete,0) - .apply("FIND_IN_SET('" + featureId + "', path)") + .and(i -> featureIds.forEach(f -> i.or().likeRight(SaasFeatureResource::getPath, f + ","))) .list(); } @@ -324,28 +381,6 @@ public class SaasFeatureResourceServiceImpl implements SaasFeatureResourceServic .one(); } - @Override - @Transactional(rollbackFor = Exception.class) - @CacheEvict(value = SaasFeatureResourceCacheService.CACHE_FEATURE_RESOURCE_TREE,allEntries = true) - public void deleteMenuFeature(Long featureId, Long operatorId) { - if (featureId == null) { - return; - } - List featureDescendant = featureResourceDao.lambdaQuery() - .eq(BaseEntity::getIsDelete,0) - .apply("FIND_IN_SET('" + featureId + "', path)") - .list(); - List featureIds = featureDescendant.stream().map(SaasFeatureResource::getId).collect(Collectors.toList()); - pgroupPermissionRelationService.deleteByFeatureIds(featureIds); - // 删除自己及自己的子集 - featureResourceDao.lambdaUpdate() - .eq(BaseEntity::getIsDelete,0) - .apply("FIND_IN_SET('" + featureId + "', path)") - .set(SaasFeatureResource::getUpdateBy, operatorId) - .set(BaseEntity::getIsDelete,1) - .update(); - } - @Override @Transactional(rollbackFor = Exception.class) @CacheEvict(value = SaasFeatureResourceCacheService.CACHE_FEATURE_RESOURCE_TREE,allEntries = true) @@ -418,23 +453,16 @@ public class SaasFeatureResourceServiceImpl implements SaasFeatureResourceServic return true; } - private List fillChildren2Root(List rootNodes, List childrenNodes) { - Map rootNodeMap = rootNodes.stream().collect(Collectors.toMap(FeatureResourceDTO::getTerminal, Function.identity(), (v1, v2) -> v1)); - for(FeatureResourceTreeNode child : childrenNodes) { - FeatureResourceTreeNode rootNode = rootNodeMap.get(child.getTerminal()); - if (child.getParentId() > 0 || Objects.isNull(rootNode)) { - continue; - } - rootNode.getChildren().add(child); - } - - return rootNodes; - } - + /** + * 菜单/页面/应用/分组,权限码每个端唯一, + * 组件权限码不校验唯一 + * @param featureResource + */ void validFeatureCode(SaasFeatureResource featureResource) { - Assert.notNull(featureResource.getFeatureCode(), "权限码code不能为空"); + Assert.notNull(featureResource.getUniCode(), "权限码code不能为空"); SaasFeatureResource exist = featureResourceDao.lambdaQuery() - .eq(SaasFeatureResource::getFeatureCode, featureResource.getFeatureCode()) + .eq(SaasFeatureResource::getTerminal, featureResource.getTerminal()) + .eq(SaasFeatureResource::getUniCode, featureResource.getUniCode()) .eq(BaseEntity::getIsDelete, 0) .one(); if (exist != null && !exist.getId().equals(featureResource.getId())) { @@ -464,20 +492,163 @@ public class SaasFeatureResourceServiceImpl implements SaasFeatureResourceServic } @Override - public List listNavMenu(String terminal) { - //按需扩展要查询的字段 + public List listByParentIdAndTerminalAndIds(Long parentId, String terminal, List featureIds) { return featureResourceDao.lambdaQuery() - .select(SaasFeatureResource::getId, - SaasFeatureResource::getParentId, - SaasFeatureResource::getFeatureCode, - SaasFeatureResource::getFeatureName, - SaasFeatureResource::getFeatureType, - SaasFeatureResource::getLinkUrl, - SaasFeatureResource::getIcon) - .eq(SaasFeatureResource::getStatus, FeatureResourceStatus.NORMAL.getCode()) - .eq(SaasFeatureResource::getFeatureType, FeatureResourceType.MENU.getCode()) - .eq(SaasFeatureResource::getTerminal, terminal) - .orderByAsc(SaasFeatureResource::getDisplayOrder) + .eq(BaseEntity::getIsDelete, 0) + .eq(Objects.nonNull(parentId), SaasFeatureResource::getParentId, parentId) + .eq(StringUtils.isNotBlank(terminal), SaasFeatureResource::getTerminal, terminal) + .in(CollectionUtil.isNotEmpty(featureIds), SaasFeatureResource::getId, featureIds) .list(); } + + @Override + public List list(PageSaasFeatureResourceReq param) { + return PageConverter.drainAll(pageNumber -> { + param.setPage(pageNumber); + param.setPageSize(500); + return page(param); + }); + } + + @Override + public PageResp page(PageSaasFeatureResourceReq param) { + + String parentPath = resolveParentPath(param); + if (Objects.nonNull(param.getParentId()) && StringUtils.isBlank(parentPath)) { + return param.toEmpty(); + } + + QueryWrapper wrapper = QueryWrapperHelper.fromBean(param, SaasFeatureResource.class); + wrapper.eq("is_delete", 0); + wrapper.likeRight(StringUtils.isNotBlank(parentPath), "path", parentPath); + + if (CollectionUtils.isNotEmpty(param.getPaths())) { + wrapper.and(j -> { + for (String path : param.getPaths()) { + j.or(k -> k.likeRight("path", path)); + } + }); + } + + IPage page = this.page(PageConverter.toMybatis(param, SaasFeatureResource.class), wrapper); + + Map> uniCodeFeatureCodeMap = listFeatureCodes(param, page.getRecords()); + + return PageConverter.toResp(page, e -> from(e, uniCodeFeatureCodeMap)); + } + + private SaasFeatureResourceResp from(SaasFeatureResource featureResource, + Map> uniCodeFeatureCodeMap) { + SaasFeatureResourceResp saasFeatureResourceResp = SaasFeatureResourceResp.builder().build(); + BeanUtils.copyProperties(featureResource, saasFeatureResourceResp); + + saasFeatureResourceResp.setFeatureCodes(uniCodeFeatureCodeMap.get(featureResource.getUniCode())); + return saasFeatureResourceResp; + } + + private Map> listFeatureCodes(PageSaasFeatureResourceReq param, + List saasFeatureResources) { + + if (CollectionUtils.isEmpty(saasFeatureResources) || BooleanUtils.isNotTrue(param.getNeedFeatureCodes())) { + return Collections.emptyMap(); + } + + List uniCodes = Lists.transform(saasFeatureResources, SaasFeatureResource::getUniCode); + PageElementFeatureResourceRelationReq pageElementFeatureResourceRelationReq = PageElementFeatureResourceRelationReq.builder() + .featureResourceUniCodes(uniCodes) + .build(); + return saasPageElementFeatureResourceRelationService.list(pageElementFeatureResourceRelationReq) + .stream() + .collect(Collectors.groupingBy(SaasPageElementFeatureResourceRelation::getFeatureResourceUniCode, + Collectors.mapping(SaasPageElementFeatureResourceRelation::getPageElementCode, Collectors.toSet()))); + + } + + private String resolveParentPath(PageSaasFeatureResourceReq param) { + if (Objects.isNull(param.getParentId())) { + return null; + } + + SaasFeatureResource parent = this.getById(param.getParentId()); + if (Objects.isNull(parent)) { + return null; + } + + return parent.getPath(); + } + + private void fillPageElement2PageFeatureResource(List featureResourceTreeNodes, List allUniCodes) { + Map> uniCodeElementsMap = saasPageElementService.getByTerminalAndUniCodes(featureResourceTreeNodes.get(0).getTerminal(), allUniCodes) + .stream().collect(Collectors.groupingBy(PageElementBasicDTO::getUniCode)); + + recursiveSetPageElement(featureResourceTreeNodes, uniCodeElementsMap); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @CacheEvict(value = SaasFeatureResourceCacheService.CACHE_FEATURE_RESOURCE_TREE,allEntries = true) + public void deleteFeatureResource(DeleteFeatureResourceReq param) { + + SaasFeatureResource featureResource = this.getById(param.getFeatureId()); + Axssert.checkNonNull(featureResource, FEATURE_RESOURCE_NOT_FOUND); + + List deleteFeatureResource = this.list(PageSaasFeatureResourceReq.builder() + .path(featureResource.getPath()) + .build()); + + // 删除自己及自己的子集 + this.removeByIds(deleteFeatureResource.stream() + .map(SaasFeatureResourceResp::getId) + .collect(Collectors.toList())); + + deletePermissionRelations(deleteFeatureResource); + } + + private void deletePermissionRelations(List deleteFeatureResource) { + + PagePgroupPermissionRelationReq pagePgroupPermissionRelationReq = PagePgroupPermissionRelationReq.builder() + .featureIds(Lists.transform(deleteFeatureResource, SaasFeatureResourceResp::getId)) + .type(NEW_FEATURE) + .build(); + List permissionRelations = saasPgroupPermissionRelationService.list(pagePgroupPermissionRelationReq); + + if (CollectionUtils.isEmpty(permissionRelations)) { + return; + } + SaasPgroupPermissionRelationService.DeleteParam deleteParam = SaasPgroupPermissionRelationService.DeleteParam.builder() + .ids(Lists.transform(permissionRelations, SaasPgroupPermissionRelation::getId)) + .build(); + saasPgroupPermissionRelationService.delete(deleteParam); + } + + private List filterBlankMenu(List treeList) { + return treeList.stream().filter(this::recursionFilterNode).collect(Collectors.toList()); + } + + private boolean recursionFilterNode(FeatureResourceTreeNode node) { + boolean matched = FeatureResourceType.PAGE.getCode().equals(node.getFeatureType()) + || FeatureResourceType.COMPONENT.getCode().equals(node.getFeatureType()) + || FeatureResourceType.MENU_PARTITION_GROUP.getCode().equals(node.getFeatureType()); + if (CollectionUtils.isEmpty(node.getNodeChildren())) { + return matched; + } + // 过滤子节点 + List filterChildren = node.getChildren().stream().filter(this::recursionFilterNode).collect(Collectors.toList()); + // 重置子节点 + node.setChildren(filterChildren); + if (CollectionUtils.isEmpty(filterChildren)) { + return matched; + } + return true; + } + + private void recursiveSetPageElement(List featureResourceTreeNodes, Map> uniCodeElementsMap) { + if (org.apache.commons.collections4.CollectionUtils.isEmpty(featureResourceTreeNodes)) { + return; + } + featureResourceTreeNodes.forEach(e -> { + e.setPageElements(uniCodeElementsMap.get(e.getUniCode())); + recursiveSetPageElement(e.getChildren(), uniCodeElementsMap); + }); + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasPageElementFeatureResourceRelationServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasPageElementFeatureResourceRelationServiceImpl.java new file mode 100644 index 00000000..abb33ff7 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasPageElementFeatureResourceRelationServiceImpl.java @@ -0,0 +1,43 @@ +package cn.axzo.tyr.server.service.impl; + +import cn.axzo.foundation.dao.support.converter.PageConverter; +import cn.axzo.foundation.dao.support.mysql.QueryWrapperHelper; +import cn.axzo.foundation.page.PageResp; +import cn.axzo.tyr.client.model.req.PageElementFeatureResourceRelationReq; +import cn.axzo.tyr.server.repository.entity.SaasPageElementFeatureResourceRelation; +import cn.axzo.tyr.server.repository.mapper.SaasPageElementFeatureResourceRelationMapper; +import cn.axzo.tyr.server.service.SaasPageElementFeatureResourceRelationService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.function.Function; + +@Slf4j +@Service +public class SaasPageElementFeatureResourceRelationServiceImpl extends ServiceImpl + implements SaasPageElementFeatureResourceRelationService { + + @Override + public List list(PageElementFeatureResourceRelationReq param) { + return PageConverter.drainAll(pageNumber -> { + param.setPage(pageNumber); + param.setPageSize(500); + return page(param); + }); + } + + @Override + public PageResp page(PageElementFeatureResourceRelationReq param) { + + QueryWrapper wrapper = QueryWrapperHelper.fromBean(param, SaasPageElementFeatureResourceRelation.class); + wrapper.eq("is_delete", 0); + + IPage page = this.page(PageConverter.toMybatis(param, SaasPageElementFeatureResourceRelation.class), wrapper); + + return PageConverter.toResp(page, Function.identity()); + } +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasPageElementServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasPageElementServiceImpl.java new file mode 100644 index 00000000..80c7e091 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasPageElementServiceImpl.java @@ -0,0 +1,436 @@ +package cn.axzo.tyr.server.service.impl; + +import cn.axzo.basics.common.BeanMapper; +import cn.axzo.basics.common.util.AssertUtil; +import cn.axzo.basics.common.util.StopWatchUtil; +import cn.axzo.basics.profiles.api.UserProfileServiceApi; +import cn.axzo.basics.profiles.dto.basic.PersonProfileDto; +import cn.axzo.foundation.exception.BusinessException; +import cn.axzo.framework.domain.page.PageResp; +import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import cn.axzo.tyr.client.common.enums.FeatureResourceType; +import cn.axzo.tyr.client.common.enums.PageElementFeatureResourceRelationTypeEnum; +import cn.axzo.tyr.client.common.enums.PageElementTypeEnum; +import cn.axzo.tyr.client.common.enums.PermissionRelationOperateLogSceneEnum; +import cn.axzo.tyr.client.model.req.*; +import cn.axzo.tyr.client.model.res.GetUserHasPermissionPageElementResp; +import cn.axzo.tyr.client.model.res.IdentityAuthRes; +import cn.axzo.tyr.client.model.res.PageElementBasicDTO; +import cn.axzo.tyr.client.model.res.PageElementResp; +import cn.axzo.tyr.server.model.RelationOperateLogResourceBindElementDO; +import cn.axzo.tyr.server.repository.dao.SaasFeatureResourceDao; +import cn.axzo.tyr.server.repository.dao.SaasPageElementDao; +import cn.axzo.tyr.server.repository.dao.SaasPageElementFeatureResourceRelationDao; +import cn.axzo.tyr.server.repository.entity.*; +import cn.axzo.tyr.server.service.SaasPageElementService; +import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationOperateLogService; +import cn.axzo.tyr.server.service.TyrSaasAuthService; +import cn.axzo.tyr.server.util.RpcInternalUtil; +import cn.azxo.framework.common.constatns.Constants; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.MDC; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/6/18 + */ +@RefreshScope +@Slf4j +@Service +@RequiredArgsConstructor +public class SaasPageElementServiceImpl implements SaasPageElementService { + + private final SaasPageElementDao saasPageElementDao; + private final SaasPageElementFeatureResourceRelationDao saasPageElementFeatureResourceRelationDao; + private final SaasFeatureResourceDao saasFeatureResourceDao; + private final TyrSaasAuthService tyrSaasAuthService; + private final SaasPgroupPermissionRelationOperateLogService saasPgroupPermissionRelationOperateLogService; + private final UserProfileServiceApi userProfileServiceApi; + + @Qualifier("asyncExecutor") + @Autowired + private ExecutorService asyncExecutor; + @Value("${not.auth.featureCodes:}") + private Set notAuthFeatureCodes; + @Value("#{'${pageElement.report.secretKeys:1qaz@WSX}'.split(',')}") + private List reportSecretKeys; + + @Override + @Transactional(rollbackFor = Exception.class) + public void report(PageElementReportReq request) { + StopWatchUtil watch = StopWatchUtil.createStarted("saasPageElement-report"); + watch.start("report"); +// AssertUtil.isTrue(reportSecretKeys.contains(request.getSecretKey()), "上报秘钥有误。"); + if (!reportSecretKeys.contains(request.getSecretKey())) { + throw new BusinessException("403", "上报秘钥有误。"); + } + // 根据端删除所有的元素 + saasPageElementDao.deleteAllByTerminal(request.getTerminal()); + + List pageElements = processParam2Entities(request); + // 减少数据库的IO,分批写入 + Lists.partition(pageElements,500).forEach(saasPageElementDao::saveBatch); + + // 页面路由处理,如果绑定了saas_feature_resource,页面的路由更新到对应的字段,为性能考虑,异步处理 + CompletableFuture.runAsync(() -> processPageLinkUrl(request), asyncExecutor).whenComplete((t, ex) -> log.info("更新表saas_feature_resource的页面路由完成。")); + + watch.stop(); + log.info("saasPageElement-report cost:{} , ms:{}", watch.prettyPrint(), watch.getTotalTimeMillis()); + } + + @Override + public List getPageElement(GetPageElementReq request) { + SaasFeatureResource saasFeatureResource = saasFeatureResourceDao.lambdaQuery() + .eq(BaseEntity::getIsDelete,0) + .eq(SaasFeatureResource::getId, request.getFeatureResourceId()) + .one(); + AssertUtil.notNull(saasFeatureResource, "页面或组件信息不存在"); + + List relations = saasPageElementFeatureResourceRelationDao.listByUniCodeAndTerminal(Lists.newArrayList(saasFeatureResource.getUniCode()), saasFeatureResource.getTerminal(), request.getRelationTypes()); + if (CollectionUtils.isEmpty(relations)) { + return Collections.emptyList(); + } + List selectedPageElements = saasPageElementDao.listByCodes(relations.stream().map(SaasPageElementFeatureResourceRelation::getPageElementCode).collect(Collectors.toList()), saasFeatureResource.getTerminal()); + if (CollectionUtils.isEmpty(selectedPageElements)) { + return Collections.emptyList(); + } + List selectedIds = selectedPageElements.stream().map(BaseEntity::getId).collect(Collectors.toList()); + + // 如果查询不仅仅是已勾选的,需要把已勾选的页面下未选择的组件也返回 + if (!Boolean.TRUE.equals(request.getQuerySelectedOnly())) { + List pageTypeCodes = selectedPageElements.stream().filter(e -> PageElementTypeEnum.PAGE.getCode().equals(e.getType())).map(SaasPageElement::getGroupCode).collect(Collectors.toList()); + if (CollectionUtils.isNotEmpty(pageTypeCodes)) { + List notSelectedComponents = saasPageElementDao.listByGroupCodesAndExcludeIds(pageTypeCodes, selectedIds, saasFeatureResource.getTerminal(), null); + if (CollectionUtils.isNotEmpty(notSelectedComponents)) { + selectedPageElements.addAll(notSelectedComponents); + } + } + } + return change2PageElementResp(selectedPageElements, selectedIds); + } + + @Override + public List getByTerminalAndUniCodes(String terminal, List featureResourceUniCodes) { + List relations = saasPageElementFeatureResourceRelationDao.listByUniCodeAndTerminal(featureResourceUniCodes, terminal, null); + if (CollectionUtils.isEmpty(relations)) { + return Collections.emptyList(); + } + Map pageElementMap = saasPageElementDao.listByCodes(relations.stream().map(SaasPageElementFeatureResourceRelation::getPageElementCode).collect(Collectors.toList()), terminal) + .stream().collect(Collectors.toMap(SaasPageElement::getCode, Function.identity(), (v1, v2) -> v1)); + + return relations.stream().map(e -> { + SaasPageElement pageElement = pageElementMap.get(e.getPageElementCode()); + if (Objects.isNull(pageElement)) { + return null; + } + return PageElementBasicDTO.builder() + .id(pageElement.getId()) + .uniCode(e.getFeatureResourceUniCode()) + .code(pageElement.getCode()) + .name(pageElement.getName()) + .relationType(e.getType()) + .build(); + }).filter(Objects::nonNull).collect(Collectors.toList()); + } + + @Override + public void modifyPageElementRelation(ModifyPageElementRelationDTO modifyPageElementRelation) { + if (StringUtils.isBlank(modifyPageElementRelation.getTerminal()) + || StringUtils.isBlank(modifyPageElementRelation.getFeatureResourceUniCode())) { + log.warn("addPageElementRelation param error, param:{}", JSONUtil.toJsonStr(modifyPageElementRelation)); + return; + } + + // 删除组件已有的绑定关系 + saasPageElementFeatureResourceRelationDao.deleteByTerminalAndUniCodes(modifyPageElementRelation.getTerminal(), + Lists.newArrayList(modifyPageElementRelation.getFeatureResourceUniCode()), Lists.newArrayList(modifyPageElementRelation.getRelationType()), modifyPageElementRelation.getOperatorId()); + + // 保存新的绑定关系 + if (CollectionUtils.isNotEmpty(modifyPageElementRelation.getPageElementCodes())) { + List relations = modifyPageElementRelation.getPageElementCodes().stream().map(e -> SaasPageElementFeatureResourceRelation.builder() + .pageElementCode(e) + .featureResourceUniCode(modifyPageElementRelation.getFeatureResourceUniCode()) + .terminal(modifyPageElementRelation.getTerminal()) + .type(modifyPageElementRelation.getRelationType()) + .createBy(modifyPageElementRelation.getOperatorId()) + .build()).collect(Collectors.toList()); + saasPageElementFeatureResourceRelationDao.saveBatch(relations); + } + + // 保存操作日志 + try { + saveOperateLog(modifyPageElementRelation); + } catch (Exception e) { + log.warn("save operate log error", e); + } + } + + @Override + public void deleteRelationByTerminalAndUniCodes(String terminal, List featureResourceUniCodes, Long operatorId) { + if (StringUtils.isBlank(terminal) || CollectionUtils.isEmpty(featureResourceUniCodes)) { + log.warn("deleteRelationByTerminalAndUniCode param error, terminal:{}, uniCodes:{}", terminal, featureResourceUniCodes); + return; + } + saasPageElementFeatureResourceRelationDao.deleteByTerminalAndUniCodes(terminal, featureResourceUniCodes, Lists.newArrayList(PageElementFeatureResourceRelationTypeEnum.DEFAULT.getValue()), operatorId); + } + + @Override + public PageResp page(PageQueryElementReq request) { + IPage page = saasPageElementDao.lambdaQuery() + .eq(SaasPageElement::getTerminal, request.getTerminal()) + .in(CollectionUtils.isNotEmpty(request.getElementTypes()), SaasPageElement::getType, request.getElementTypes()) + .and(StringUtils.isNotBlank(request.getSearchKey()), w -> w.like(SaasPageElement::getCode, request.getSearchKey()) + .or().like(SaasPageElement::getName, request.getSearchKey()) + .or().like(SaasPageElement::getLinkUrl, request.getSearchKey())) + .page(new Page<>(request.getPage(), request.getPageSize())); + List list = BeanMapper.copyList(page.getRecords(), PageElementResp.class); + addComponentElement2Page(request.getTerminal(), list); + return PageResp.list(page.getCurrent(), page.getSize(), page.getTotal(), list); + } + + @Override + public GetUserHasPermissionPageElementResp getUserHasPermissionPageElement(GetUserHasPermissionPageElementReq request) { + if (CollectionUtils.isNotEmpty(notAuthFeatureCodes) && notAuthFeatureCodes.contains(request.getPageElementCode())) { + List pageElementCodes = saasPageElementDao.listByGroupCodesAndExcludeIds(Lists.newArrayList(request.getPageElementCode()),null, request.getTerminal(), null) + .stream().map(SaasPageElement::getCode).distinct().collect(Collectors.toList()); + return GetUserHasPermissionPageElementResp.builder().pageElementCodes(pageElementCodes).build(); + } + + // 页面绑定的所有关系(包含页面路由的绑定、默认绑定关系) + List relations = saasPageElementFeatureResourceRelationDao.listByPageElementCode(request.getPageElementCode(), request.getTerminal(), Lists.newArrayList(PageElementFeatureResourceRelationTypeEnum.PAGE_ROUTE.getValue(), PageElementFeatureResourceRelationTypeEnum.DEFAULT.getValue())); + if (CollectionUtils.isEmpty(relations)) { + log.warn("绑定关系不存在,页面元素编码:{}", request.getPageElementCode()); + return null; + } + List resultRelations = Lists.newArrayList(relations); + // 路由页面向下找,非路由页面向上找。 + List pageRouteRelations = relations.stream().filter(relation -> PageElementFeatureResourceRelationTypeEnum.PAGE_ROUTE.getValue().equals(relation.getType())).collect(Collectors.toList()); + if (CollectionUtils.isNotEmpty(pageRouteRelations)) { + // 查询默认路由页面及页面下组件的绑定关系 + List saasFeatureResources = listDescendantByUniCodeAndTerminal(request.getTerminal(), pageRouteRelations.stream().map(SaasPageElementFeatureResourceRelation::getFeatureResourceUniCode).collect(Collectors.toList())); + if (CollectionUtils.isEmpty(saasFeatureResources)) { + return null; + } + List resourceRelations = saasPageElementFeatureResourceRelationDao.listByUniCodeAndTerminal(saasFeatureResources.stream().map(SaasFeatureResource::getUniCode).collect(Collectors.toList()), request.getTerminal(), null); + if (CollectionUtils.isNotEmpty(resourceRelations)) { + // 这里会存在重复的数据,下面在过滤转为Set的时候做了去重 + resultRelations.addAll(resourceRelations); + } + } else { + log.warn("页面路由的绑定关系不存在,页面元素编码:{}", request.getPageElementCode()); + // 查询页面的所有元素 + List pageElementCodes = saasPageElementDao.listByGroupCodesAndExcludeIds(relations.stream().map(SaasPageElementFeatureResourceRelation::getPageElementCode).collect(Collectors.toList()),null, request.getTerminal(), null) + .stream().map(SaasPageElement::getCode).distinct().collect(Collectors.toList()); + + resultRelations = saasPageElementFeatureResourceRelationDao.listByUniCodeAndTerminal(relations.stream().map(SaasPageElementFeatureResourceRelation::getFeatureResourceUniCode).collect(Collectors.toList()), request.getTerminal(), null) + .stream().filter(e -> pageElementCodes.contains(e.getPageElementCode())).distinct().collect(Collectors.toList()); + } + + // 过滤用户有权限的featureCodes + IdentityAuthRes res = tyrSaasAuthService.findIdentityAuthMix(IdentityAuthReq.builder() + .personId(request.getPersonId()) + .workspaceOusPairs(Lists.newArrayList(IdentityAuthReq.WorkspaceOuPair.builder().ouId(request.getOuId()).workspaceId(request.getWorkspaceId()).build())) + .terminal(Lists.newArrayList(request.getTerminal())) + .featureCode(resultRelations.stream().map(SaasPageElementFeatureResourceRelation::getPageElementCode).collect(Collectors.toSet())) + .build()); + if (Objects.isNull(res) || CollectionUtils.isEmpty(res.getPermissions())) { + return null; + } + + List hasPermissionFeatureCodes = res.getPermissions().stream().filter(e -> CollectionUtils.isNotEmpty(e.getPermissionPoint())) + .map(e -> e.getPermissionPoint().stream().map(IdentityAuthRes.PermissionPoint::getFeatureCode).collect(Collectors.toList())).flatMap(List::stream).distinct().collect(Collectors.toList()); + log.info("getUserHasPermissionPageElement personId:{} pageElementCode:{}, hasPermissionFeatureCodes:{}", request.getPersonId(), request.getPageElementCode(), hasPermissionFeatureCodes); + + return GetUserHasPermissionPageElementResp.builder() + .pageElementCodes(hasPermissionFeatureCodes) + .build(); + } + + private List processParam2Entities(PageElementReportReq request) { + List saasPageElements = Lists.newArrayList(); + + for (PageElementReportReq.PageElement pageElement : request.getPageElements()) { + saasPageElements.add(SaasPageElement.builder() + .terminal(request.getTerminal()) + .groupCode(pageElement.getCode()) + .code(pageElement.getCode()) + .type(pageElement.getType()) + .name(pageElement.getName()) + .linkUrl(pageElement.getLinkUrl()) + .createName(request.getCreateName()) + .build()); + + if (CollectionUtils.isNotEmpty(pageElement.getChildren())) { + saasPageElements.addAll(pageElement.getChildren().stream().map(e -> SaasPageElement.builder() + .terminal(request.getTerminal()) + .groupCode(pageElement.getCode()) + .code(e.getCode()) + .type(e.getType()) + .name(e.getName()) + .createName(request.getCreateName()) + .build()).collect(Collectors.toList())); + } + } + return saasPageElements; + } + + private void processPageLinkUrl(PageElementReportReq request) { + for (PageElementReportReq.PageElement pageElement : request.getPageElements()) { + if (StringUtils.isBlank(pageElement.getLinkUrl())) { + continue; + } + List relations = saasPageElementFeatureResourceRelationDao.listByPageElementCode(pageElement.getCode(), request.getTerminal(), Lists.newArrayList(PageElementFeatureResourceRelationTypeEnum.PAGE_ROUTE.getValue())); + if (CollectionUtils.isEmpty(relations)) { + continue; + } + List saasFeatureResources = saasFeatureResourceDao.lambdaQuery() + .eq(SaasFeatureResource::getUniCode, relations.get(0).getFeatureResourceUniCode()) + .eq(SaasFeatureResource::getTerminal, request.getTerminal()) + .list(); + if (CollectionUtils.isEmpty(saasFeatureResources)) { + continue; + } + SaasFeatureResource saasFeatureResource = saasFeatureResources.get(0); + if (!FeatureResourceType.PAGE.getCode().equals(saasFeatureResource.getFeatureType()) + || pageElement.getLinkUrl().equals(saasFeatureResource.getLinkUrl())) { + continue; + } + saasFeatureResourceDao.lambdaUpdate() + .eq(BaseEntity::getId, saasFeatureResource.getId()) + .set(SaasFeatureResource::getLinkUrl, pageElement.getLinkUrl()) + .update(); + } + } + + private List change2PageElementResp(List pageElements, List selectedIds) { + if (CollectionUtils.isEmpty(pageElements)) { + return Collections.emptyList(); + } + + List pageTypeElements = pageElements.stream().filter(e -> PageElementTypeEnum.PAGE.getCode().equals(e.getType())) + .map(e -> PageElementResp.builder() + .id(e.getId()) + .code(e.getCode()) + .name(e.getName()) + .type(e.getType()) + .selected(selectedIds.contains(e.getId())) + .children(Lists.newArrayList()) + .build()).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(pageTypeElements)) { + return Collections.emptyList(); + } + + Map pageTypeCodeMap = pageTypeElements.stream().collect(Collectors.toMap(PageElementResp::getCode, Function.identity(), (v1, v2) -> v1)); + pageElements.stream().filter(e -> PageElementTypeEnum.COMPONENT.getCode().equals(e.getType())).forEach(e -> { + PageElementResp pageTypeElement = pageTypeCodeMap.get(e.getGroupCode()); + if (Objects.nonNull(pageTypeElement)) { + pageTypeElement.getChildren().add(PageElementResp.builder() + .id(e.getId()) + .code(e.getCode()) + .name(e.getName()) + .type(e.getType()) + .selected(selectedIds.contains(e.getId())) + .build()); + } + }); + + return pageTypeElements; + } + + private void addComponentElement2Page(String terminal, List list) { + if (CollectionUtils.isEmpty(list)) { + return; + } + List pageTypeCodes = Lists.newArrayList(); + List excludeIds = Lists.newArrayList(); + Map pageTypeCodeMap = Maps.newHashMap(); + for (PageElementResp element : list) { + if (PageElementTypeEnum.PAGE.getCode().equals(element.getType())) { + pageTypeCodes.add(element.getGroupCode()); + pageTypeCodeMap.put(element.getCode(), element); + } + excludeIds.add(element.getId()); + element.setChildren(Lists.newArrayList()); + } + + List componentElements = saasPageElementDao.listByGroupCodesAndExcludeIds(pageTypeCodes, excludeIds, terminal, Lists.newArrayList(PageElementTypeEnum.COMPONENT.getCode())); + if (CollectionUtils.isEmpty(componentElements)) { + return; + } + + componentElements.forEach(e -> { + PageElementResp pageTypeElement = pageTypeCodeMap.get(e.getGroupCode()); + if (Objects.nonNull(pageTypeElement)) { + pageTypeElement.getChildren().add(PageElementResp.builder() + .id(e.getId()) + .groupCode(e.getGroupCode()) + .code(e.getCode()) + .name(e.getName()) + .type(e.getType()) + .build()); + } + }); + } + + public List listDescendantByUniCodeAndTerminal(String terminal, List uniCodes) { + List pageResources = saasFeatureResourceDao.lambdaQuery() + .eq(SaasFeatureResource::getTerminal, terminal) + .in(SaasFeatureResource::getUniCode, uniCodes) + .eq(SaasFeatureResource::getFeatureType, FeatureResourceType.PAGE.getCode()) + .orderByDesc(BaseEntity::getId) + .list(); + if (CollectionUtils.isEmpty(pageResources)) { + log.warn("页面资源(featureResource)不存在。 terminal:{} uniCodes:{}", terminal, uniCodes); + return Collections.emptyList(); + } + return saasFeatureResourceDao.lambdaQuery() + .eq(SaasFeatureResource::getTerminal, terminal) + .and(w -> pageResources.forEach(p -> w.or().likeRight(SaasFeatureResource::getPath, p.getPath()))) + .list(); + } + + private void saveOperateLog(ModifyPageElementRelationDTO relation) { + List personProfileDtos = RpcInternalUtil.rpcListProcessor(() -> userProfileServiceApi.getPersonProfiles(Lists.newArrayList(relation.getOperatorId())), "查询用户信息", relation.getOperatorId()).getData(); + PersonProfileDto operator = org.apache.commons.collections4.CollectionUtils.isEmpty(personProfileDtos) ? null : personProfileDtos.get(0); + + RelationOperateLogResourceBindElementDO operateAfter = RelationOperateLogResourceBindElementDO.builder() + .uniCode(relation.getFeatureResourceUniCode()) + .featureCodes(relation.getPageElementCodes()) + .build(); + SaasPgroupPermissionRelationOperateLog operateLog = SaasPgroupPermissionRelationOperateLog.builder() + .tableName(SaasPgroupPermissionRelationOperateLogServiceImpl.TABLE_NAME_PAGE_ELEMENT) + .scene(PermissionRelationOperateLogSceneEnum.OMS_FEATURE_RESOURCE_BIND_ELEMENT.getValue()) + .sceneId(relation.getFeatureResourceUniCode()) + .createBy(relation.getOperatorId()) + .createByName(Objects.isNull(operator) ? "" : operator.getRealName()) + .traceId(MDC.get(Constants.CTX_LOG_ID_MDC)) + .requestData(JSONObject.toJSONString(relation)) + .operateData(JSONObject.toJSONString(Lists.newArrayList(operateAfter))) + .createByRole(JSONObject.toJSONString(saasPgroupPermissionRelationOperateLogService.getPersonBasicRoles(relation.getOperatorId()))) + .build(); + + saasPgroupPermissionRelationOperateLogService.batchSave(Lists.newArrayList(operateLog)); + } +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasPgroupPermissionRelationOperateLogServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasPgroupPermissionRelationOperateLogServiceImpl.java new file mode 100644 index 00000000..97c06eda --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasPgroupPermissionRelationOperateLogServiceImpl.java @@ -0,0 +1,102 @@ +package cn.axzo.tyr.server.service.impl; + +import cn.axzo.basics.profiles.api.UserProfileServiceApi; +import cn.axzo.basics.profiles.dto.basic.PersonProfileDto; +import cn.axzo.framework.auth.domain.ContextInfo; +import cn.axzo.framework.auth.domain.ContextInfoHolder; +import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import cn.axzo.tyr.client.common.enums.PermissionRelationOperateLogSceneEnum; +import cn.axzo.tyr.client.model.req.PermissionOperateLogReq; +import cn.axzo.tyr.server.model.BasicRoleDO; +import cn.axzo.tyr.server.model.RelationOperateLogProductBindResourceDO; +import cn.axzo.tyr.server.repository.dao.SaasPgroupPermissionRelationOperateLogDao; +import cn.axzo.tyr.server.repository.dao.SaasRoleDao; +import cn.axzo.tyr.server.repository.dao.SaasRoleUserRelationDao; +import cn.axzo.tyr.server.repository.entity.SaasFeatureResource; +import cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelationOperateLog; +import cn.axzo.tyr.server.repository.entity.SaasRoleUserRelation; +import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationOperateLogService; +import cn.axzo.tyr.server.util.RpcInternalUtil; +import cn.azxo.framework.common.constatns.Constants; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Lists; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.slf4j.MDC; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author likunpeng + * @version 1.0 + * @date 2024/7/24 + */ +@Slf4j +@Service +@AllArgsConstructor +public class SaasPgroupPermissionRelationOperateLogServiceImpl implements SaasPgroupPermissionRelationOperateLogService { + + public static final String TABLE_NAME_FEATURE_RESOURCE = "saas_feature_resource"; + public static final String TABLE_NAME_ROLE = "saas_role"; + public static final String TABLE_NAME_PAGE_ELEMENT = "saas_page_element"; + public static final String TABLE_NAME_PRODUCT = "product_module"; + public static final String TABLE_NAME_PRODUCT_MODULE_FEATURE_RELATION = "saas_product_module_feature_relation"; + public static final String TABLE_NAME_SAAS_ROLE_GROUP = "saas_role_group"; + public static final String TABLE_NAME_SAAS_FEATURE = "saas_feature"; + + private final SaasPgroupPermissionRelationOperateLogDao saasPgroupPermissionRelationOperateLogDao; + private final SaasRoleUserRelationDao roleUserRelationDao; + private final SaasRoleDao saasRoleDao; + private final UserProfileServiceApi userProfileServiceApi; + + @Override + public void batchSave(List logs) { + if (CollectionUtils.isEmpty(logs)) { + return; + } + saasPgroupPermissionRelationOperateLogDao.saveBatch(logs); + } + + @Override + public List getPersonBasicRoles(Long personId) { + if (Objects.isNull(personId)) { + return Collections.emptyList(); + } + ContextInfo contextInfo = ContextInfoHolder.get(); + if (Objects.isNull(contextInfo)) { + log.warn("no contextInfo, personId:{}", personId); + return Collections.emptyList(); + } + List relations = roleUserRelationDao.queryByPersonId(personId, contextInfo.getWorkspaceId(), contextInfo.getOuId()); + if (CollectionUtils.isEmpty(relations)) { + return Collections.emptyList(); + } + + return saasRoleDao.lambdaQuery().in(BaseEntity::getId, relations.stream().map(SaasRoleUserRelation::getRoleId).collect(Collectors.toList())).list() + .stream().map(e -> BasicRoleDO.builder().roleId(e.getId()).roleCode(e.getRoleCode()).roleName(e.getName()).build()).collect(Collectors.toList()); + } + + @Override + public void save(PermissionOperateLogReq req) { + List personProfileDtos = RpcInternalUtil.rpcListProcessor(() -> userProfileServiceApi.getPersonProfiles(Lists.newArrayList(req.getOperatorId())), "查询用户信息", req.getOperatorId()).getData(); + PersonProfileDto operator = CollectionUtils.isEmpty(personProfileDtos) ? null : personProfileDtos.get(0); + + SaasPgroupPermissionRelationOperateLog operateLog = SaasPgroupPermissionRelationOperateLog.builder() + .tableName(req.getTableName()) + .scene(req.getScene()) + .sceneId(req.getSceneId()) + .createBy(req.getOperatorId()) + .createByName(Objects.isNull(operator) ? "" : operator.getRealName()) + .traceId(MDC.get(Constants.CTX_LOG_ID_MDC)) + .requestData(Objects.nonNull(req.getRequestData()) ? JSONObject.toJSONString(req.getRequestData()) : null) + .operateData(Objects.nonNull(req.getOperateData()) ? JSONObject.toJSONString(req.getOperateData()) : null) + .createByRole(JSONObject.toJSONString(getPersonBasicRoles(req.getOperatorId()))) + .build(); + batchSave(Lists.newArrayList(operateLog)); + } +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasPgroupPermissionRelationServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasPgroupPermissionRelationServiceImpl.java index 9ad46caf..92a3a2e3 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasPgroupPermissionRelationServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasPgroupPermissionRelationServiceImpl.java @@ -1,43 +1,68 @@ package cn.axzo.tyr.server.service.impl; import cn.axzo.basics.common.constant.enums.TableIsDeleteEnum; +import cn.axzo.foundation.dao.support.converter.PageConverter; +import cn.axzo.foundation.dao.support.mysql.QueryWrapperHelper; +import cn.axzo.foundation.page.PageResp; +import cn.axzo.framework.auth.domain.TerminalInfo; +import cn.axzo.tyr.client.common.enums.FeatureResourceType; +import cn.axzo.tyr.client.model.req.PagePgroupPermissionRelationReq; import cn.axzo.tyr.server.repository.dao.SaasPgroupPermissionRelationDao; import cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation; +import cn.axzo.tyr.server.repository.mapper.SaasPgroupPermissionRelationMapper; import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationService; import cn.hutool.core.collection.CollectionUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.common.collect.Lists; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; +import java.util.function.Function; import java.util.stream.Collectors; +import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.NEW_FEATURE; + @Slf4j @Service @RequiredArgsConstructor -public class SaasPgroupPermissionRelationServiceImpl implements SaasPgroupPermissionRelationService { +public class SaasPgroupPermissionRelationServiceImpl + extends ServiceImpl + implements SaasPgroupPermissionRelationService { private final SaasPgroupPermissionRelationDao saasPgroupPermissionRelationDao; @Override @Transactional(rollbackFor = Exception.class) - public void saveOrUpdate(List groupIds, List relations) { + public void saveOrUpdate(UpsertPermissionRelationParam param) { + + List groupIds = param.getGroupIds(); + List relations = param.getRelations(); if (CollectionUtils.isEmpty(groupIds) && CollectionUtils.isEmpty(relations)) { return; } // 移除权限点 - if (CollectionUtils.isEmpty(groupIds)) { - groupIds = relations.stream().map(SaasPgroupPermissionRelation::getGroupId).distinct().sorted().collect(Collectors.toList()); + if (CollectionUtils.isEmpty(param.getGroupIds())) { + groupIds = param.getRelations().stream().map(SaasPgroupPermissionRelation::getGroupId).distinct().sorted().collect(Collectors.toList()); } else if (Objects.isNull(relations)){ relations = new ArrayList<>(); } + + // 因为同一个角色会绑定两个菜单资源树的情况,不能相互覆盖,因为项企分离时,CMS做了新旧版本灰度,新版本使用saas_feature_resource,旧版本使用saas_feature + // 所以SaasPgroupPermissionRelation加了type做区分,写的时候要按照type来隔离 List exists = saasPgroupPermissionRelationDao.lambdaQuery() .in(SaasPgroupPermissionRelation::getGroupId, groupIds) - .eq(SaasPgroupPermissionRelation::getIsDelete, TableIsDeleteEnum.NORMAL.value).list(); + .eq(SaasPgroupPermissionRelation::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .eq(SaasPgroupPermissionRelation::getType, param.getType()) + .list(); Collection insertList = CollectionUtils.subtract(relations, exists); Collection deleteList = CollectionUtils.subtract(exists, relations); if (CollectionUtils.isNotEmpty(insertList)) { @@ -48,13 +73,44 @@ public class SaasPgroupPermissionRelationServiceImpl implements SaasPgroupPermis } } + @Override + public PageResp page(PagePgroupPermissionRelationReq param) { + QueryWrapper wrapper = QueryWrapperHelper.fromBean(param, SaasPgroupPermissionRelation.class); + wrapper.eq("is_delete", 0); + + if (CollectionUtils.isNotEmpty(param.getFeatureResourceTypes())) { + wrapper.in("feature_type", Lists.transform(param.getFeatureResourceTypes(), FeatureResourceType::getCode)); + } + + // 目前只有新版本的CMS端产品配置时才冗余了terminal + if (Objects.equals(NEW_FEATURE, param.getType()) && StringUtils.hasLength(param.getTerminal())) { + TerminalInfo terminalInfo = new TerminalInfo(param.getTerminal()); + if (terminalInfo.isCMS()) { + wrapper.eq("terminal", param.getTerminal()); + } + } + + IPage page = this.page(PageConverter.toMybatis(param, SaasPgroupPermissionRelation.class), wrapper); + + return PageConverter.toResp(page, Function.identity()); + } + + @Override + public List list(PagePgroupPermissionRelationReq param) { + return PageConverter.drainAll(pageNumber -> { + param.setPage(pageNumber); + // 减少io,后续优化 + param.setPageSize(30000); + return page(param); + }); + } + @Override @Transactional(rollbackFor = Exception.class) - public void deleteByFeatureIds(List featureIds) { - if (CollectionUtil.isEmpty(featureIds)) { + public void delete(DeleteParam param) { + if (CollectionUtil.isEmpty(param.getIds())) { return; } - saasPgroupPermissionRelationDao.removeByPermissionPointIds(featureIds); - + this.removeByIds(param.getIds()); } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasRoleGroupRelationServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasRoleGroupRelationServiceImpl.java index dafb0283..9a189b80 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasRoleGroupRelationServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasRoleGroupRelationServiceImpl.java @@ -1,14 +1,15 @@ package cn.axzo.tyr.server.service.impl; import cn.axzo.basics.common.constant.enums.TableIsDeleteEnum; -import cn.axzo.pokonyan.dao.converter.PageConverter; -import cn.axzo.pokonyan.dao.mysql.QueryWrapperHelper; +import cn.axzo.foundation.dao.support.converter.PageConverter; +import cn.axzo.foundation.dao.support.mysql.QueryWrapperHelper; +import cn.axzo.foundation.page.PageResp; import cn.axzo.tyr.server.repository.dao.SaasRoleGroupRelationDao; import cn.axzo.tyr.server.repository.entity.SaasRoleGroupRelation; import cn.axzo.tyr.server.repository.mapper.SaasRoleGroupRelationMapper; import cn.axzo.tyr.server.service.SaasRoleGroupRelationService; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -19,6 +20,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.Collection; import java.util.List; +import java.util.function.Function; import java.util.stream.Collectors; @Slf4j @@ -63,10 +65,11 @@ public class SaasRoleGroupRelationServiceImpl extends ServiceImpl page(PageSaasRoleGroupRelationParam param) { + public PageResp page(PageSaasRoleGroupRelationParam param) { QueryWrapper wrapper = QueryWrapperHelper.fromBean(param, SaasRoleGroupRelation.class); wrapper.eq("is_delete", 0); + IPage page = this.page(PageConverter.toMybatis(param, SaasRoleGroupRelation.class), wrapper); - return this.page(PageConverter.convertToMybatis(param, SaasRoleGroupRelation.class), wrapper); + return PageConverter.toResp(page, Function.identity()); } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasRoleGroupServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasRoleGroupServiceImpl.java index cffa57b4..296a37a5 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasRoleGroupServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasRoleGroupServiceImpl.java @@ -2,21 +2,46 @@ package cn.axzo.tyr.server.service.impl; import cn.axzo.basics.common.constant.enums.TableIsDeleteEnum; import cn.axzo.basics.common.exception.ServiceException; +import cn.axzo.foundation.dao.support.converter.PageConverter; +import cn.axzo.foundation.dao.support.mysql.QueryWrapperHelper; +import cn.axzo.foundation.exception.Axssert; +import cn.axzo.foundation.page.PageResp; +import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import cn.axzo.tyr.client.common.enums.PermissionRelationOperateLogSceneEnum; +import cn.axzo.tyr.client.model.req.ListSaasRoleGroupParam; +import cn.axzo.tyr.client.model.req.PageSaasRoleGroupParam; +import cn.axzo.tyr.client.model.req.PermissionOperateLogReq; import cn.axzo.tyr.client.model.req.QuerySaasRoleGroupReq; +import cn.axzo.tyr.client.model.req.SaasRoleGroupDeleteRequest; +import cn.axzo.tyr.client.model.res.SaasRoleGroupDTO; +import cn.axzo.tyr.client.model.res.SaasRoleRes; import cn.axzo.tyr.client.model.vo.SaasRoleGroupVO; +import cn.axzo.tyr.server.model.BasicRoleDO; import cn.axzo.tyr.server.repository.dao.SaasRoleGroupDao; import cn.axzo.tyr.server.repository.dao.SaasRoleGroupRelationDao; +import cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelationOperateLog; import cn.axzo.tyr.server.repository.entity.SaasRoleGroup; import cn.axzo.tyr.server.repository.entity.SaasRoleGroupRelation; import cn.axzo.tyr.server.repository.mapper.SaasRoleGroupMapper; +import cn.axzo.tyr.server.service.RoleService; +import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationOperateLogService; +import cn.axzo.tyr.server.service.SaasRoleGroupRelationService; import cn.axzo.tyr.server.service.SaasRoleGroupService; +import cn.azxo.framework.common.constatns.Constants; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.common.collect.Lists; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; +import org.slf4j.MDC; +import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -24,19 +49,29 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; +import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.function.Function; import java.util.stream.Collectors; +import static cn.axzo.tyr.server.config.exception.BizResultCode.CANT_DELETE_ROLE_GROUP; + @Slf4j @RequiredArgsConstructor @Service public class SaasRoleGroupServiceImpl extends ServiceImpl implements SaasRoleGroupService { + + private final SaasRoleGroupMapper saasRoleGroupMapper; private final SaasRoleGroupDao saasRoleGroupDao; private final SaasRoleGroupRelationDao saasRoleGroupRelationDao; + private final SaasRoleGroupRelationService saasRoleGroupRelationService; + private final RoleService roleService; + private final SaasPgroupPermissionRelationOperateLogService saasPgroupPermissionRelationOperateLogService; @Override public List getList(QuerySaasRoleGroupReq req) { @@ -109,16 +144,41 @@ public class SaasRoleGroupServiceImpl extends ServiceImpl ids) { + SaasRoleGroupDeleteRequest request = new SaasRoleGroupDeleteRequest(); + request.setIds(ids); + deleteV2(request); + } + + /** + * 只有当分组下面角色为空合分组下没有其他分组时才能删除 + * @param request + */ + @Override + public void deleteV2(SaasRoleGroupDeleteRequest request) { + List ids = request.getIds(); if (CollectionUtils.isEmpty(ids)) { return; } @@ -129,7 +189,49 @@ public class SaasRoleGroupServiceImpl extends ServiceImpl 0) { throw new ServiceException("该角色分组有关联角色,不能进行删除!"); } + + QuerySaasRoleGroupReq querySaasRoleGroupReq = QuerySaasRoleGroupReq.builder() + .parentIds(new HashSet<>(ids)) + .build(); + List childrenRoleGroups = saasRoleGroupDao.query(querySaasRoleGroupReq); + Axssert.check(CollectionUtils.isEmpty(childrenRoleGroups), CANT_DELETE_ROLE_GROUP); + saasRoleGroupDao.delete(ids); + + try { + saveOperateLogForDelete(request); + } catch (Exception e) { + log.warn("error save operate log", e); + } + } + + private void saveOperateLogForDelete(SaasRoleGroupDeleteRequest request) { + List ids = request.getIds(); + if (CollectionUtils.isEmpty(ids)) { + return; + } + Map id2Group = saasRoleGroupMapper.getByIdsDeleteAware(ids) + .stream() + .collect(Collectors.toMap(BaseEntity::getId, Function.identity())); + ArrayList logs = new ArrayList<>(); + List personBasicRoles = saasPgroupPermissionRelationOperateLogService + .getPersonBasicRoles(request.determineOperator().getPersonId()); + for (Long id : ids) { + SaasRoleGroup group = id2Group.get(id); + SaasPgroupPermissionRelationOperateLog log = SaasPgroupPermissionRelationOperateLog.builder() + .tableName(SaasPgroupPermissionRelationOperateLogServiceImpl.TABLE_NAME_SAAS_ROLE_GROUP) + .scene(PermissionRelationOperateLogSceneEnum.OLD_OMS_ROLE_GROUP__DELETE.getValue()) + .sceneId(id + "") + .createBy(request.determineOperator().getPersonId()) + .createByName(request.determineOperator().getRealName()) + .traceId(MDC.get(Constants.CTX_LOG_ID_MDC)) + .requestData(JSONObject.toJSONString(request)) + .operateData(JSONObject.toJSONString(group)) + .createByRole(JSONObject.toJSONString(personBasicRoles)) + .build(); + logs.add(log); + } + saasPgroupPermissionRelationOperateLogService.batchSave(logs); } private SaasRoleGroup validAndBuildGroup(SaasRoleGroupVO req) { @@ -192,7 +294,8 @@ public class SaasRoleGroupServiceImpl extends ServiceImpl 0) { throw new ServiceException("角色分组编码已经存在"); @@ -244,4 +349,63 @@ public class SaasRoleGroupServiceImpl extends ServiceImpl list(ListSaasRoleGroupParam param) { + return PageConverter.drainAll(pageNumber -> { + PageSaasRoleGroupParam pageParam = PageSaasRoleGroupParam.builder().build(); + BeanUtils.copyProperties(param, pageParam); + pageParam.setPage(pageNumber); + pageParam.setPageSize(500); + return page(pageParam); + }); + + } + + @Override + public PageResp page(PageSaasRoleGroupParam param) { + QueryWrapper wrapper = QueryWrapperHelper.fromBean(param, SaasRoleGroup.class); + wrapper.eq("is_delete", 0); + + IPage page = this.page(PageConverter.toMybatis(param, SaasRoleGroup.class), wrapper); + + Map> roles = listRoles(param, page.getRecords()); + + return PageConverter.toResp(page, (record) -> from(record, roles)); + } + + private SaasRoleGroupDTO from(SaasRoleGroup saasRoleGroup, + Map> roles) { + SaasRoleGroupDTO saasRoleGroupDTO = SaasRoleGroupDTO.builder().build(); + BeanUtils.copyProperties(saasRoleGroup, saasRoleGroupDTO); + + saasRoleGroupDTO.setSaasRoles(roles.get(saasRoleGroupDTO.getId())); + return saasRoleGroupDTO; + } + + private Map> listRoles(PageSaasRoleGroupParam param, + List saasRoleGroups) { + if (CollectionUtils.isEmpty(saasRoleGroups) || BooleanUtils.isNotTrue(param.getNeedRole())) { + return Collections.emptyMap(); + } + + SaasRoleGroupRelationService.ListSaasRoleGroupRelationParam listSaasRoleGroupRelationParam = SaasRoleGroupRelationService.ListSaasRoleGroupRelationParam.builder().build(); + listSaasRoleGroupRelationParam.setSaasRoleGroupIds(Lists.transform(saasRoleGroups, SaasRoleGroup::getId)); + List saasRoleGroupRelations = saasRoleGroupRelationService.list(listSaasRoleGroupRelationParam); + + List roleIds = Lists.transform(saasRoleGroupRelations, SaasRoleGroupRelation::getRoleId); + + if (CollectionUtils.isEmpty(roleIds)) { + return Collections.emptyMap(); + } + RoleService.ListSaasRoleParam listSaasRoleParam = RoleService.ListSaasRoleParam.builder().build(); + listSaasRoleParam.setRoleIds(roleIds); + Map roles = roleService.list(listSaasRoleParam).stream() + .collect(Collectors.toMap(SaasRoleRes::getId, Function.identity())); + + return saasRoleGroupRelations.stream() + .collect(Collectors.groupingBy(SaasRoleGroupRelation::getSaasRoleGroupId, + LinkedHashMap::new, + Collectors.mapping(e -> roles.get(e.getRoleId()), Collectors.toList()))); + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasRoleUserRelationServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasRoleUserRelationServiceImpl.java index 3a7c4ef7..c16cb012 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasRoleUserRelationServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/SaasRoleUserRelationServiceImpl.java @@ -2,28 +2,47 @@ package cn.axzo.tyr.server.service.impl; import cn.axzo.basics.common.BeanMapper; import cn.axzo.basics.common.constant.enums.TableIsDeleteEnum; +import cn.axzo.basics.profiles.api.UserProfileServiceApi; +import cn.axzo.basics.profiles.dto.basic.PersonProfileDto; +import cn.axzo.foundation.dao.support.converter.PageConverter; +import cn.axzo.foundation.dao.support.mysql.QueryWrapperHelper; import cn.axzo.framework.domain.page.PageResp; import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; import cn.axzo.tyr.client.common.enums.RoleTypeEnum; import cn.axzo.tyr.client.model.roleuser.dto.SaasRoleUserDTO; +import cn.axzo.tyr.client.model.roleuser.dto.SaasRoleUserV2DTO; +import cn.axzo.tyr.client.model.roleuser.req.ListRoleUserRelationParam; +import cn.axzo.tyr.client.model.roleuser.req.PageRoleUserRelationParam; import cn.axzo.tyr.client.model.roleuser.req.RoleUserParam; import cn.axzo.tyr.server.repository.dao.SaasRoleDao; import cn.axzo.tyr.server.repository.dao.SaasRoleUserRelationDao; import cn.axzo.tyr.server.repository.entity.SaasRole; import cn.axzo.tyr.server.repository.entity.SaasRoleUserRelation; +import cn.axzo.tyr.server.repository.mapper.SaasRoleUserRelationMapper; +import cn.axzo.tyr.server.service.RoleService; import cn.axzo.tyr.server.service.SaasRoleUserRelationService; +import cn.axzo.tyr.server.util.RpcInternalUtil; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollectionUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.common.collect.Lists; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.BooleanUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; import javax.annotation.Resource; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.function.Function; import java.util.stream.Collectors; @@ -33,7 +52,8 @@ import java.util.stream.Collectors; */ @Slf4j @Service -public class SaasRoleUserRelationServiceImpl implements SaasRoleUserRelationService { +public class SaasRoleUserRelationServiceImpl extends ServiceImpl + implements SaasRoleUserRelationService { @Resource private SaasRoleUserRelationDao saasRoleUserRelationDao; @@ -41,6 +61,11 @@ public class SaasRoleUserRelationServiceImpl implements SaasRoleUserRelationServ @Resource private SaasRoleDao saasRoleDao; + @Autowired + private UserProfileServiceApi userProfileServiceApi; + @Autowired + private RoleService roleService; + @Override public List list(RoleUserParam param) { // TODO jhy 角色查询 需要验证标准角色和自定义角色的查询逻辑 @@ -75,7 +100,7 @@ public class SaasRoleUserRelationServiceImpl implements SaasRoleUserRelationServ .eq(Objects.nonNull(param.getWorkspaceId()), SaasRoleUserRelation::getWorkspaceId, param.getWorkspaceId()) .eq(Objects.nonNull(param.getOuId()), SaasRoleUserRelation::getOuId, param.getOuId()) .in(CollectionUtil.isNotEmpty(param.getOuIds()), SaasRoleUserRelation::getOuId, param.getOuIds()) - .in(CollectionUtil.isNotEmpty(param.getWorkspaceIds()), SaasRoleUserRelation::getWorkspaceId, param.getWorkspaceId()) + .in(CollectionUtil.isNotEmpty(param.getWorkspaceIds()), SaasRoleUserRelation::getWorkspaceId, param.getWorkspaceIds()) .in(CollectionUtil.isNotEmpty(param.getRoleIds()), SaasRoleUserRelation::getRoleId, param.getRoleIds()) .in(CollectionUtil.isNotEmpty(param.getIdentityIds()), SaasRoleUserRelation::getIdentityId, param.getIdentityIds()) .eq(BaseEntity::getIsDelete, TableIsDeleteEnum.NORMAL.value); @@ -99,4 +124,128 @@ public class SaasRoleUserRelationServiceImpl implements SaasRoleUserRelationServ .collect(Collectors.toList()); return PageResp.list(param.getPage(), param.getPageSize(), page.getTotal(), list); } + + @Override + public List listV2(ListRoleUserRelationParam param) { + return PageConverter.drainAll(pageNumber -> { + PageRoleUserRelationParam pageParam = PageRoleUserRelationParam.builder().build(); + BeanUtils.copyProperties(param, pageParam); + pageParam.setPage(pageNumber); + pageParam.setPageSize(500); + return page(pageParam); + }); + } + + @Override + public cn.axzo.foundation.page.PageResp page(PageRoleUserRelationParam param) { + QueryWrapper wrapper = QueryWrapperHelper.fromBean(param, SaasRoleUserRelation.class); + wrapper.eq("is_delete", 0); + + if (!CollectionUtils.isEmpty(param.getWorkspaceOuPairs())) { + wrapper.and(j -> { + for (ListRoleUserRelationParam.WorkspaceOuPair workspaceOuPair : param.getWorkspaceOuPairs()) { + j.or(k -> { + k.eq(Objects.nonNull(workspaceOuPair.getOuId()), "ou_id", workspaceOuPair.getOuId()); + k.eq(Objects.nonNull(workspaceOuPair.getWorkspaceId()), "workspace_id", workspaceOuPair.getWorkspaceId()); + }); + } + }); + } + + IPage page = this.page(PageConverter.toMybatis(param, SaasRoleUserRelation.class), wrapper); + + Map saasRoleUsers = listSaasRoleUser(param, page.getRecords()); + + Map saasRoles = listSaasRole(param, page.getRecords()); + + return PageConverter.toResp(page, (record) -> from(record, saasRoleUsers, saasRoles)); + } + + private Map listSaasRoleUser(PageRoleUserRelationParam param, + List saasRoleUserRelations) { + if (CollectionUtils.isEmpty(saasRoleUserRelations) || BooleanUtils.isNotTrue(param.getNeedUsers())) { + return Collections.emptyMap(); + } + + List personIds = saasRoleUserRelations.stream() + .map(SaasRoleUserRelation::getNaturalPersonId) + .distinct() + .collect(Collectors.toList()); + + if (CollectionUtils.isEmpty(personIds)) { + return Collections.emptyMap(); + } + + List> partition = Lists.partition(personIds, 1000); + + // 返回字段太多,所以截取部分字段返回,后续需要新增的手动增加 + return partition.stream() + .map(e -> RpcInternalUtil.rpcListProcessor(() -> userProfileServiceApi.postPersonProfiles(e), "查询用户信息", e).getData()) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .collect(Collectors.toMap(PersonProfileDto::getId, this::from, (f, s) -> f)); + + } + + private SaasRoleUserV2DTO.SaasRoleUser from(PersonProfileDto personProfileDto) { + if (personProfileDto == null) { + return null; + } + + return SaasRoleUserV2DTO.SaasRoleUser.builder() + .personId(personProfileDto.getId()) + .realName(personProfileDto.getRealName()) + .build(); + } + + private SaasRoleUserV2DTO from(SaasRoleUserRelation saasRoleUserRelation, + Map saasRoleUsers, + Map saasRoles) { + + SaasRoleUserV2DTO.SaasRoleUser saasRoleUser = SaasRoleUserV2DTO.SaasRoleUser.builder() + .ouId(saasRoleUserRelation.getOuId()) + .workspaceId(saasRoleUserRelation.getWorkspaceId()) + .personId(saasRoleUserRelation.getNaturalPersonId()) + .realName(Optional.ofNullable(saasRoleUsers.get(saasRoleUserRelation.getNaturalPersonId())) + .map(SaasRoleUserV2DTO.SaasRoleUser::getRealName) + .orElse(null)) + .build(); + + return SaasRoleUserV2DTO.builder() + .id(saasRoleUserRelation.getId()) + .roleId(saasRoleUserRelation.getRoleId()) + .saasRoleUser(saasRoleUser) + .saasRole(saasRoles.get(saasRoleUserRelation.getRoleId())) + .build(); + } + + private Map listSaasRole(PageRoleUserRelationParam param, + List saasRoleUserRelations) { + if (CollectionUtils.isEmpty(saasRoleUserRelations) || BooleanUtils.isNotTrue(param.getNeedRole())) { + return Collections.emptyMap(); + } + + List roleIds = saasRoleUserRelations.stream() + .map(SaasRoleUserRelation::getRoleId) + .distinct() + .collect(Collectors.toList()); + + RoleService.ListSaasRoleParam listSaasRoleParam = RoleService.ListSaasRoleParam.builder() + .roleIds(roleIds) + .needPermissionOld(param.getNeedRolePermissionOld()) + .featureIds(param.getFeatureIds()) + .featureResourceTypes(param.getFeatureResourceTypes()) + .needPermissionRelation(param.getNeedPermissionRelation()) + .type(param.getType()) + .terminal(param.getTerminal()) + .needPermission(param.getNeedPermission()) + .build(); + return roleService.list(listSaasRoleParam).stream() + .map(e -> { + SaasRoleUserV2DTO.SaasRole saasRole = SaasRoleUserV2DTO.SaasRole.builder().build(); + BeanUtils.copyProperties(e, saasRole); + return saasRole; + }) + .collect(Collectors.toMap(SaasRoleUserV2DTO.SaasRole::getId, Function.identity())); + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/TyrSaasAuthServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/TyrSaasAuthServiceImpl.java index fccbda58..4c6f9522 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/TyrSaasAuthServiceImpl.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/TyrSaasAuthServiceImpl.java @@ -1,73 +1,124 @@ package cn.axzo.tyr.server.service.impl; import cn.axzo.basics.common.BeanMapper; +import cn.axzo.basics.common.constant.enums.TableIsDeleteEnum; import cn.axzo.framework.domain.ServiceException; -import cn.axzo.framework.domain.web.result.ApiResult; -import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; import cn.axzo.pokonyan.util.TraceSupplier; import cn.axzo.thrones.client.saas.ServicePkgClient; import cn.axzo.thrones.client.saas.entity.serivicepgkproduct.ServicePkgProduct; import cn.axzo.thrones.client.saas.entity.servicepkg.ServicePkgDetailRes; +import cn.axzo.tyr.client.common.enums.FeatureResourceAuthType; import cn.axzo.tyr.client.common.enums.RoleTypeEnum; import cn.axzo.tyr.client.common.enums.WorkspaceJoinType; import cn.axzo.tyr.client.model.enums.DelegatedType; import cn.axzo.tyr.client.model.enums.IdentityType; +import cn.axzo.tyr.client.model.enums.WorkspaceTypeCodeEnum; import cn.axzo.tyr.client.model.permission.PermissionPointListQueryRequest; import cn.axzo.tyr.client.model.permission.PermissionPointTreeNode; import cn.axzo.tyr.client.model.product.ProductFeatureRelationVO; -import cn.axzo.tyr.client.model.req.*; +import cn.axzo.tyr.client.model.req.CheckIdentityPermissionReq; +import cn.axzo.tyr.client.model.req.FeatureIdPair; +import cn.axzo.tyr.client.model.req.IdentityAuthReq; +import cn.axzo.tyr.client.model.req.ListIdentityFromPermissionReq; +import cn.axzo.tyr.client.model.req.ListPermissionFromFeatureReq; +import cn.axzo.tyr.client.model.req.ListPermissionFromIdentityReq; +import cn.axzo.tyr.client.model.req.ListPermissionFromRoleGroupReq; +import cn.axzo.tyr.client.model.req.OUWorkspacePair; +import cn.axzo.tyr.client.model.req.PageElementFeatureResourceRelationReq; +import cn.axzo.tyr.client.model.req.PagePgroupPermissionRelationReq; +import cn.axzo.tyr.client.model.req.PageSaasFeatureResourceReq; +import cn.axzo.tyr.client.model.req.PermissionCheckReq; +import cn.axzo.tyr.client.model.req.QueryPermissionByIdsReq; +import cn.axzo.tyr.client.model.req.QuerySaasRoleReq; +import cn.axzo.tyr.client.model.req.WorkspacePermissionIdentityReq; import cn.axzo.tyr.client.model.res.IdentityAuthRes; import cn.axzo.tyr.client.model.res.ListIdentityFromPermissionResp; import cn.axzo.tyr.client.model.res.ListPermissionFromRoleGroupResp; import cn.axzo.tyr.client.model.res.QueryIdentityByPermissionResp; -import cn.axzo.tyr.client.model.res.SimpleFeatureInfo; +import cn.axzo.tyr.client.model.res.SaasFeatureResourceResp; +import cn.axzo.tyr.client.model.res.SaasRoleRes; import cn.axzo.tyr.client.model.res.SimplePermissionPointResp; +import cn.axzo.tyr.client.model.roleuser.dto.SaasRoleUserV2DTO; +import cn.axzo.tyr.client.model.roleuser.req.ListRoleUserRelationParam; import cn.axzo.tyr.client.model.vo.SaasPermissionGroupVO; -import cn.axzo.tyr.client.model.roleuser.dto.SuperAminInfoResp; -import cn.axzo.tyr.client.model.roleuser.req.SuperAdminParam; import cn.axzo.tyr.client.model.vo.SaasRoleVO; import cn.axzo.tyr.server.model.FilterRoleAuth; import cn.axzo.tyr.server.model.PermissionCacheKey; -import cn.axzo.tyr.server.repository.entity.*; +import cn.axzo.tyr.server.repository.dao.SaasFeatureDao; +import cn.axzo.tyr.server.repository.dao.SaasPgroupRoleRelationDao; +import cn.axzo.tyr.server.repository.dao.SaasProductModuleFeatureRelationDao; +import cn.axzo.tyr.server.repository.entity.ProductFeatureInfo; +import cn.axzo.tyr.server.repository.entity.ProductFeatureQuery; +import cn.axzo.tyr.server.repository.entity.RolePermission; +import cn.axzo.tyr.server.repository.entity.SaasFeature; +import cn.axzo.tyr.server.repository.entity.SaasFeatureResource; +import cn.axzo.tyr.server.repository.entity.SaasPageElementFeatureResourceRelation; +import cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation; +import cn.axzo.tyr.server.repository.entity.SaasPgroupRoleRelation; +import cn.axzo.tyr.server.repository.entity.SaasProductModuleFeatureRelation; +import cn.axzo.tyr.server.repository.entity.SaasRole; +import cn.axzo.tyr.server.repository.entity.SaasRoleGroup; +import cn.axzo.tyr.server.repository.entity.SaasRoleUserRelation; +import cn.axzo.tyr.server.repository.entity.SaasRoleWithUser; import cn.axzo.tyr.server.repository.mapper.TyrSaasAuthMapper; import cn.axzo.tyr.server.service.PermissionCacheService; import cn.axzo.tyr.server.service.PermissionPointService; import cn.axzo.tyr.server.service.ProductFeatureRelationService; import cn.axzo.tyr.server.service.RoleService; +import cn.axzo.tyr.server.service.SaasFeatureResourceService; +import cn.axzo.tyr.server.service.SaasPageElementFeatureResourceRelationService; +import cn.axzo.tyr.server.service.SaasPgroupPermissionRelationService; +import cn.axzo.tyr.server.service.SaasRoleGroupRelationService; import cn.axzo.tyr.server.service.SaasRoleGroupService; +import cn.axzo.tyr.server.service.SaasRoleUserRelationService; import cn.axzo.tyr.server.service.TyrSaasAuthService; +import cn.axzo.tyr.server.service.WorkspaceProductService; +import cn.axzo.tyr.server.util.FeatureCodeUtil; import cn.axzo.tyr.server.util.KeyUtil; import cn.axzo.tyr.server.utils.RpcExternalUtil; -import cn.axzo.tyr.server.utils.RpcInternalUtil; import cn.azxo.framework.common.model.CommonResponse; import cn.azxo.framework.common.utils.LogUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.date.StopWatch; import cn.hutool.core.lang.Pair; -import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.StrUtil; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; -import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.stream.Collectors; +import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.NEW_FEATURE; +import static cn.axzo.tyr.server.repository.entity.SaasPgroupPermissionRelation.OLD_FEATURE; import static cn.axzo.tyr.server.util.RpcInternalUtil.checkAndGetData; /** @@ -88,13 +139,22 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { private final ServicePkgClient servicePkgClient; @Qualifier("authExecutor") @Autowired - private Executor executor; + private Executor executor; private final ProductFeatureRelationService productFeatureRelationService; private final PermissionPointService permissionPointService; private final PermissionCacheService permissionCacheService; private final SaasRoleGroupService roleGroupService; - + private final SaasRoleUserRelationService saasRoleUserRelationService; + private final SaasFeatureDao saasFeatureDao; + private final SaasProductModuleFeatureRelationDao saasProductModuleFeatureRelationDao; + private final WorkspaceProductService workspaceProductService; + private final SaasFeatureResourceService saasFeatureResourceService; + private final SaasPageElementFeatureResourceRelationService saasPageElementFeatureResourceRelationService; + private final SaasPgroupPermissionRelationService saasPgroupPermissionRelationService; + private final SaasRoleGroupRelationService saasRoleGroupRelationService; + private final FeatureCodeUtil featureCodeUtil; + private final SaasPgroupRoleRelationDao saasPgroupRoleRelationDao; /** * 通过身份查询人员权限 @@ -313,27 +373,35 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { return permissionSet.containsAll(checkCodes); } - private IdentityAuthRes findIdentityAuth(IdentityAuthReq identityAuthReq) { //用户角色关系 List saasRoleUserRelations = listRoleUserRelations(identityAuthReq); - + if (CollectionUtils.isEmpty(saasRoleUserRelations)) { + log.warn("no user role relations found"); + return identityAuthReq.toEmpty(); + } Set realWorkspaceId = saasRoleUserRelations.stream().map(SaasRoleUserRelation::getWorkspaceId).collect(Collectors.toSet()); //工作台对应产品 key = workspaceId - CompletableFuture>> workspacePermissionPointFuture = CompletableFuture - .supplyAsync(TraceSupplier.create(() -> productFeatureRelationService.getByWorkspace(realWorkspaceId)), executor); + CompletableFuture> workspacePermissionPointFuture = CompletableFuture + .supplyAsync(TraceSupplier.create(() -> { + WorkspaceProductService.WorkspaceProductParam workspaceProductParam = WorkspaceProductService.WorkspaceProductParam.builder() + .workspaceIds(realWorkspaceId) + .build(); + return workspaceProductService.listWorkspaceProduct(workspaceProductParam); + }), executor); //查询工作台下授予的角色和权限 List owRoles = listRolesWithPermission(saasRoleUserRelations, identityAuthReq); - Map> workspaceProductPermissionMap = workspacePermissionPointFuture.join(); + Map workspaceProductPermissionMap = workspacePermissionPointFuture.join().stream() + .collect(Collectors.toMap(WorkspaceProductService.WorkspaceProduct::getWorkspaceId, Function.identity())); List> futureList = new ArrayList<>(); for (OUWRoleInfo owRoleInfo : owRoles) { // 工作台的产品权限点 - List productFeatureRelationVOS = workspaceProductPermissionMap.get(owRoleInfo.getWorkspaceId()); + WorkspaceProductService.WorkspaceProduct workspaceProduct = workspaceProductPermissionMap.get(owRoleInfo.getWorkspaceId()); //构建每个工作台的实际权限点 - futureList.add(CompletableFuture.supplyAsync(TraceSupplier.create(() -> buildPermissions(owRoleInfo, productFeatureRelationVOS)), executor) + futureList.add(CompletableFuture.supplyAsync(TraceSupplier.create(() -> buildPermissions(owRoleInfo, workspaceProduct)), executor) .exceptionally(t -> { LogUtil.error("获取角色对应权限失败", t); throw new ServiceException(t); @@ -351,43 +419,44 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { return result; } - private IdentityAuthRes.WorkspacePermission buildPermissions(OUWRoleInfo ouwRoleInfo, List productFeatures) { + private IdentityAuthRes.WorkspacePermission buildPermissions(OUWRoleInfo ouwRoleInfo, WorkspaceProductService.WorkspaceProduct workspaceProduct) { IdentityAuthRes.WorkspacePermission resultPermission = IdentityAuthRes.WorkspacePermission.builder() .workspaceId(ouwRoleInfo.getWorkspaceId()) .ouId(ouwRoleInfo.getOuId()) .build(); - if (CollectionUtil.isEmpty(productFeatures)) { + if (Objects.isNull(workspaceProduct) || CollectionUtil.isEmpty(workspaceProduct.getSaasProductModuleFeatureRelations())) { log.warn("no product features found for workspace :{}", ouwRoleInfo.getWorkspaceId()); return resultPermission; } - Set roles = ouwRoleInfo.getRoles(); + Set roles = ouwRoleInfo.getRoles(); if (CollectionUtil.isEmpty(roles)) { log.warn("no roles for ou:{} workspace:{}", ouwRoleInfo.getOuId(), ouwRoleInfo.getWorkspaceId()); return resultPermission; } + List productFeatures = workspaceProduct.getSaasProductModuleFeatureRelations(); + // 因为存在同时有saas_feature和saas_feature_resource的权限,所以要返回type,根据type解析code //超管和管理员权限 - Pair> adminPermissions = buildAdminPermission(ouwRoleInfo, productFeatures); + Pair> adminPermissions = buildAdminPermission(ouwRoleInfo, productFeatures); //标准角和自定义角色权限 - Set normalPermissions = buildNormalPermission(ouwRoleInfo, productFeatures); - Set allPermissionIds = new HashSet<>(); - allPermissionIds.addAll(adminPermissions.getValue()); - allPermissionIds.addAll(normalPermissions); + Set normalPermissions = buildNormalPermission(ouwRoleInfo, productFeatures); + Set allPermissions = Sets.newHashSet(); + allPermissions.addAll(adminPermissions.getValue()); + allPermissions.addAll(normalPermissions); //查询权限点及父级权限点 - List allPermissionPoint = permissionPointService.listPermissionByIds( - QueryPermissionByIdsReq.builder() - .ids(allPermissionIds) - .includeParent(true) - .build()); + List allOldPermissionPoint = listOldFeatures(allPermissions); + + List newPermissionPoints = listNewFeatures(allPermissions); + //组装返回值 //是否超管 resultPermission.setSuperAdmin(BooleanUtil.isTrue(adminPermissions.getKey())); //权限数据 - resultPermission.getPermissionPoint().addAll(allPermissionPoint.stream() + resultPermission.getPermissionPoint().addAll(allOldPermissionPoint.stream() .map(permissionPointTreeNode -> IdentityAuthRes.PermissionPoint.builder() .featureCode(permissionPointTreeNode.getCode()) .featureId(permissionPointTreeNode.getId()) @@ -395,60 +464,160 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { .build()) .collect(Collectors.toList())); + resultPermission.getPermissionPoint().addAll(newPermissionPoints); return resultPermission; } - private Set buildNormalPermission(OUWRoleInfo userRoleInfoMap, List productFeatures) { + private List listNewFeatures(Set featureWrappers) { + List featureIds = featureWrappers.stream() + .filter(e -> Objects.equals(e.getType(), NEW_FEATURE)) + .map(FeatureWrapper::getFeatureId) + .collect(Collectors.toList()); + + if (CollectionUtils.isEmpty(featureIds)) { + return Collections.emptyList(); + } + + // 因为新版本配置权限点的时候,会在选中某个权限节点时,把所有父节点也冗余到权限里,所以只需要查询权限点信息 + return saasFeatureResourceService.list(PageSaasFeatureResourceReq.builder() + .ids(featureIds) + .needFeatureCodes(true) + .build()) + .stream() + .filter(e -> !CollectionUtils.isEmpty(e.getFeatureCodes())) + .map(e -> + // 兼容历史情况,根据featureCode组装数据 + e.getFeatureCodes().stream() + .map(featureCode -> IdentityAuthRes.PermissionPoint.builder() + .featureCode(featureCode) + .featureId(e.getId()) + .terminal(e.getTerminal()) + .workspaceType(e.getWorkspaceType()) + .build()) + .collect(Collectors.toList())) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + + private List listOldFeatures(Set featureWrappers) { + Set featureIds = featureWrappers.stream() + .filter(e -> Objects.equals(e.getType(), OLD_FEATURE)) + .map(FeatureWrapper::getFeatureId) + .collect(Collectors.toSet()); + + if (CollectionUtils.isEmpty(featureIds)) { + return Collections.emptyList(); + } + + return permissionPointService.listPermissionByIds( + QueryPermissionByIdsReq.builder() + .ids(featureIds) + .includeParent(true) + .build()); + } + + private Set buildNormalPermission(OUWRoleInfo userRoleInfoMap, List productFeatures) { log.info("build permission for ou:{}, workspace:{}", userRoleInfoMap.getOuId(), userRoleInfoMap.getWorkspaceId()); - Set allMatchedProductFeatureIds = new HashSet<>(); - Set allAuthPermissionIds = new HashSet<>(); + Set allMatchedProductFeatures = new HashSet<>(); + Set allAuthFeatures = new HashSet<>(); + //聚合实际授权的权限:角色权限和产品权限交集 - for (SaasRoleVO role : userRoleInfoMap.getRoles()) { + for (SaasRoleRes role : userRoleInfoMap.getRoles()) { //跳过超管和管理员 if (RoleTypeEnum.SUPER_ADMIN.getValue().equals(role.getRoleType()) || RoleTypeEnum.ADMIN.getValue().equals(role.getRoleType())) { continue; } log.info("build permission for role:{}", role.getId()); - Set rolePermissionIds = role.getMatchFeature(userRoleInfoMap.getWorkspaceId(), userRoleInfoMap.ouId) - .stream() - .filter(Objects::nonNull) - .map(PermissionPointTreeNode::getPermissionPointId) - .collect(Collectors.toSet()); + + Set rolePermissions = Optional.ofNullable(role.getPermissionRelations()) + .map(e -> e.stream() + .filter(Objects::nonNull) + .map(f -> FeatureWrapper.builder() + .featureId(f.getFeatureId()) + .type(f.getType()) + .build()) + .collect(Collectors.toSet())) + .orElseGet(Sets::newHashSet); //角色标签类型匹配产品标签类型 - Set productPermissionIds = productFeatures.stream() + Set productPermissions = productFeatures.stream() .filter(productFeatureRelationVO -> Objects.equals(productFeatureRelationVO.getDictCode(), String.valueOf(role.getProductUnitType()))) - .map(ProductFeatureRelationVO::getFeatureId) + .map(e -> FeatureWrapper.builder() + .featureId(e.getFeatureId()) + .type(e.getType()) + .build()) .collect(Collectors.toSet()); - allMatchedProductFeatureIds.addAll(productPermissionIds); + allMatchedProductFeatures.addAll(productPermissions); // 产品对应权限点 与 角色权限点 取交集 - Collection resultHashAuthPointId = CollectionUtil.intersection(productPermissionIds, rolePermissionIds); + Collection resultHashAuthPointId = CollectionUtil.intersection(productPermissions, rolePermissions); if (CollectionUtil.isNotEmpty(resultHashAuthPointId)) { log.info("add auth permission for role:{}", role.getId()); - allAuthPermissionIds.addAll(resultHashAuthPointId); + allAuthFeatures.addAll(resultHashAuthPointId); } } - if (CollectionUtil.isEmpty(allMatchedProductFeatureIds)) { + if (CollectionUtil.isEmpty(allMatchedProductFeatures)) { log.info("no normal roles found"); - return allAuthPermissionIds; + return allAuthFeatures; } - // 免授权权限点统一处理 - List noNeedPermissionPoint = permissionPointService.queryList(PermissionPointListQueryRequest.builder() - .ids(new ArrayList<>(allMatchedProductFeatureIds)) - .delegatedType(DelegatedType.NO_NEED.getCode()) - .build()); - allAuthPermissionIds.addAll(noNeedPermissionPoint.stream().map(PermissionPointTreeNode::getPermissionPointId).collect(Collectors.toSet())); - return allAuthPermissionIds; + Set newFeatureNoAuth = listNoAuthFeatureResources(allMatchedProductFeatures); + + Set oldFeatureNoAuth = listNoAuthFeatures(allMatchedProductFeatures); + allAuthFeatures.addAll(newFeatureNoAuth); + allAuthFeatures.addAll(oldFeatureNoAuth); + return allAuthFeatures; } - private Pair> buildAdminPermission(OUWRoleInfo userRoleInfoMap, List productFeatures) { + private Set listNoAuthFeatures(Set featureWrappers) { + List featureIds = featureWrappers.stream() + .filter(e -> Objects.equals(e.getType(), OLD_FEATURE)) + .map(FeatureWrapper::getFeatureId) + .collect(Collectors.toList()); + + if (CollectionUtils.isEmpty(featureIds)) { + return Collections.emptySet(); + } + + return permissionPointService.queryList(PermissionPointListQueryRequest.builder() + .ids(featureIds) + .delegatedType(DelegatedType.NO_NEED.getCode()) + .build()) + .stream() + .map(e -> FeatureWrapper.builder() + .featureId(e.getPermissionPointId()) + .type(OLD_FEATURE) + .build()) + .collect(Collectors.toSet()); + } + + private Set listNoAuthFeatureResources(Set featureWrappers) { + List featureIds = featureWrappers.stream() + .filter(e -> Objects.equals(e.getType(), NEW_FEATURE)) + .map(FeatureWrapper::getFeatureId) + .collect(Collectors.toList()); + + if (CollectionUtils.isEmpty(featureIds)) { + return Collections.emptySet(); + } + + PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder() + .ids(featureIds) + .authType(FeatureResourceAuthType.ALL_ROLE.getCode()) + .build(); + return saasFeatureResourceService.list(pageSaasFeatureResourceReq).stream() + .map(e -> FeatureWrapper.builder() + .featureId(e.getId()) + .type(NEW_FEATURE) + .build()) + .collect(Collectors.toSet()); + } + + private Pair> buildAdminPermission(OUWRoleInfo userRoleInfoMap, List productFeatures) { Boolean superAdmin = false; //超管和管理员角色 - List adminRoles = userRoleInfoMap.getRoles().stream() - .filter(r -> RoleTypeEnum.SUPER_ADMIN.getValue().equals(r.getRoleType()) - || RoleTypeEnum.ADMIN.getValue().equals(r.getRoleType())) + List adminRoles = userRoleInfoMap.getRoles().stream() + .filter(r -> RoleTypeEnum.isAdmin(r.getRoleType())) .collect(Collectors.toList()); if (CollectionUtil.isEmpty(adminRoles)) { log.info("no admin roles"); @@ -458,41 +627,54 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { log.info("build admin permission for ou:{}, workspace:{}", userRoleInfoMap.getOuId(), userRoleInfoMap.getWorkspaceId()); //聚合超管和管理员的权限点: 直接取角色标签和产品标签相匹配的权限点 - Set permissionIds = new HashSet<>(); - for (SaasRoleVO adminRole : adminRoles) { + Set permissions = Sets.newHashSet(); + for (SaasRoleRes adminRole : adminRoles) { //超管:查询工作台对应产品,获取权限点, ( 权限点通过单位类型过滤) if (RoleTypeEnum.SUPER_ADMIN.getValue().equals(adminRole.getRoleType())) { superAdmin = true; } //角色标签类型匹配产品标签类型 - Set buttonPermissionPointId = productFeatures.stream() + Set permission = productFeatures.stream() .filter(productFeatureRelationVO -> Objects.equals(productFeatureRelationVO.getDictCode(), String.valueOf(adminRole.getProductUnitType()))) - .map(ProductFeatureRelationVO::getFeatureId) + .map(e -> FeatureWrapper.builder() + .featureId(e.getFeatureId()) + .type(e.getType()) + .build()) .collect(Collectors.toSet()); - if (CollectionUtil.isEmpty(buttonPermissionPointId)) { + if (CollectionUtil.isEmpty(permission)) { log.warn("empty permission for admin role:{}", adminRole.getId()); continue; } log.info("add all permissions for role:{}", adminRole.getId()); - permissionIds.addAll(buttonPermissionPointId); + permissions.addAll(permission); } - return Pair.of(superAdmin, permissionIds); + return Pair.of(superAdmin, permissions); + } + + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + static class FeatureWrapper { + private Long featureId; + + /** + * 关联类型(0:saas_feature,1:saas_feature_resource) + */ + private Integer type; } private List listRolesWithPermission(List roleUserRelations, IdentityAuthReq identityAuthReq) { //拼装参数 - Set realWorkspaceIds = new HashSet<>(); - Set realOuIds = new HashSet<>(); Set roleIds = new HashSet<>(); //按ow分组角色ID: workspaceId-ouId --> roleIds Map> owRoleIdMap = new HashMap<>(); for (SaasRoleUserRelation relation : roleUserRelations) { - realWorkspaceIds.add(relation.getWorkspaceId()); - realOuIds.add(relation.getOuId()); roleIds.add(relation.getRoleId()); String key = KeyUtil.buildKeyBySeparator(relation.getWorkspaceId(), relation.getOuId()); Set owRoleIds = owRoleIdMap.getOrDefault(key, new HashSet<>()); @@ -500,13 +682,12 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { owRoleIdMap.put(key, owRoleIds); } //获取角色和关联权限信息 - List roles = roleService.query(QuerySaasRoleReq.builder() - //角色ID - .ids(new ArrayList<>(roleIds)) - .workspaceId(new ArrayList<>(realWorkspaceIds)) - .ouId(new ArrayList<>(realOuIds)) - .includePermissionGroup(true) - .build()); + RoleService.ListSaasRoleParam listSaasRoleParam = RoleService.ListSaasRoleParam.builder() + .roleIds(Lists.newArrayList(roleIds)) + .needPermissionRelation(true) + .build(); + Map saasRoleRes = roleService.list(listSaasRoleParam).stream() + .collect(Collectors.toMap(SaasRoleRes::getId, Function.identity())); //按ow组装拥有的角色 List owRoleMap = new ArrayList<>(); @@ -521,8 +702,10 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { log.info("no roles found for ow:{}", key); owRoleInfo.setRoles(Collections.emptySet()); } else { - owRoleInfo.setRoles(roles.stream() - .filter(r -> owRoleIds.contains(r.getId())) + owRoleInfo.setRoles(owRoleIds.stream() + .map(saasRoleRes::get) + // 有saas_role_user_relation有记录,但是对应的saas_role不存在的情况 + .filter(Objects::nonNull) .collect(Collectors.toSet())); } owRoleMap.add(owRoleInfo); @@ -598,8 +781,47 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { //比较code return authRes.getPermissions().stream() .anyMatch(e -> e.getPermissionPoint() - .stream() - .anyMatch(p -> codeSet.contains(p.getFeatureCode()))); + .stream() + .anyMatch(p -> codeSet.contains(p.getFeatureCode()))); + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + static class ListSaasFeatureResourceParam { + private Set featureCodes; + + private String terminal; + } + + private List listSaasFeatureResource(ListSaasFeatureResourceParam req) { + + PageElementFeatureResourceRelationReq pageElementFeatureResourceRelationReq = PageElementFeatureResourceRelationReq.builder() + .pageElementCodes(req.getFeatureCodes()) + .terminal(req.getTerminal()) + .build(); + List relations = saasPageElementFeatureResourceRelationService.list(pageElementFeatureResourceRelationReq); + + if (CollectionUtils.isEmpty(relations)) { + log.info("not found in SaasPageElementFeatureResourceRelation, featureCodes:{},terminal:{}", + req.getFeatureCodes(), req.getTerminal()); + return Collections.emptyList(); + } + + Set uniCodes = relations.stream().map(SaasPageElementFeatureResourceRelation::getFeatureResourceUniCode).collect(Collectors.toSet()); + PageSaasFeatureResourceReq pageSaasFeatureResourceReq = PageSaasFeatureResourceReq.builder() + .uniCodes(uniCodes) + .build(); + List featureResources = saasFeatureResourceService.list(pageSaasFeatureResourceReq); + + if (CollectionUtils.isEmpty(featureResources)) { + log.info("not found in SaasFeatureResource, unicode:{}", uniCodes); + return Collections.emptyList(); + } + return saasFeatureResourceService.list(PageSaasFeatureResourceReq.builder() + .paths(featureResources.stream().map(SaasFeatureResourceResp::getPath).collect(Collectors.toSet())) + .build()); } @Override @@ -608,9 +830,19 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { result.setOuId(req.getOuId()); result.setWorkspaceId(req.getWorkspaceId()); + Set newFeatureCodes = featureCodeUtil.resolveFeatureCode(Sets.newHashSet(req.getFeatureCode())); + //code查询权限点信息 - List features = permissionPointService.listNodeWithChildrenByCode(req.getFeatureCode(), req.getTerminal()); - if (CollectionUtil.isEmpty(features)) { + List features = permissionPointService.listNodeWithChildrenByCodes(Lists.newArrayList(newFeatureCodes), req.getTerminal()); + + // 兼容新老版本,需要通过featureCode查询新版本的features,原逻辑是查询当前菜单资源的所有子数据 + ListSaasFeatureResourceParam listSaasFeatureResourceParam = ListSaasFeatureResourceParam.builder() + .featureCodes(newFeatureCodes) + .terminal(req.getTerminal()) + .build(); + List saasFeatureResources = listSaasFeatureResource(listSaasFeatureResourceParam); + + if (CollectionUtil.isEmpty(features) && CollectionUtils.isEmpty(saasFeatureResources)) { log.warn("no features data found for:{}", req.getFeatureCode()); return result; } @@ -618,20 +850,46 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { Optional freeFeature = features.stream() .filter(f -> DelegatedType.NO_NEED.sameCode(f.getDelegatedType())) .findAny(); - if (freeFeature.isPresent()) { - log.warn("free feature found :{}", freeFeature.get().getId()); + + Optional freeFeatureResource = saasFeatureResources.stream() + .filter(e -> SaasFeatureResource.AuthType.isAllRole(e.getAuthType())) + .findFirst(); + + if (freeFeature.isPresent() || freeFeatureResource.isPresent()) { + log.warn("free feature found : featureId:{}, featureResourceId:{}", + freeFeature.map(SaasFeature::getId).orElse(null), + freeFeatureResource.map(SaasFeatureResourceResp::getId).orElse(null)); throw new ServiceException("不能查询免授权权限点人员"); } Set featureIds = features.stream().map(SaasFeature::getId).collect(Collectors.toSet()); - //权限匹配 - 有该权限的工作台产品 productUnitType -> featureIds - Map> workspaceFeatureMap = matchWorkspaceFeature(req.getWorkspaceId(), featureIds); - if (CollectionUtil.isEmpty(workspaceFeatureMap)) { + Set newFeatureIds = saasFeatureResources.stream().map(SaasFeatureResourceResp::getId).collect(Collectors.toSet()); + + List featureIdPairs = Lists.newArrayList(); + if (!CollectionUtils.isEmpty(featureIds)) { + featureIdPairs.add(FeatureIdPair.builder().featureIds(featureIds).type(OLD_FEATURE).build()); + } + + if (!CollectionUtils.isEmpty(newFeatureIds)) { + featureIdPairs.add(FeatureIdPair.builder().featureIds(newFeatureIds).type(NEW_FEATURE).build()); + } + WorkspaceProductService.WorkspaceProductParam workspaceProductParam = WorkspaceProductService.WorkspaceProductParam.builder() + .workspaceIds(Sets.newHashSet(req.getWorkspaceId())) + .featureIdPairs(featureIdPairs) + .build(); + List workspaceProducts = workspaceProductService.listWorkspaceProduct(workspaceProductParam) + .stream() + .map(WorkspaceProductService.WorkspaceProduct::getSaasProductModuleFeatureRelations) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + + if (CollectionUtil.isEmpty(workspaceProducts)) { log.warn("no matched product feature in workspace"); return result; } - List matchedUsers = getWorkspaceUser(req.getWorkspaceId(), req.getOuId(), workspaceFeatureMap); + List matchedUsers = getWorkspaceUser(req.getWorkspaceId(), req.getOuId(), workspaceProducts); if (CollectionUtil.isEmpty(matchedUsers)) { return result; } @@ -639,25 +897,6 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { return result; } - private Map> matchWorkspaceFeature(Long workspaceId, Set featureIds) { - //查询工作台下产品 - List productList = checkAndGetData(servicePkgClient.listProductInWorkSpace(workspaceId)); - if (CollectionUtil.isEmpty(productList)) { - log.warn("------trace-L-I-F-P----> no product found for workspace"); - return Collections.emptyMap(); - } - //产品包含的权限-过滤参建类型 和 feature - return productFeatureRelationService.queryOnCondition(ProductFeatureQuery.builder() - .productIds(productList.stream() - .map(ServicePkgProduct::getProductId) - .collect(Collectors.toSet())) - .featureIds(featureIds) - .build()) - .stream() - .collect(Collectors.groupingBy(r -> Integer.valueOf(r.getDictCode()), - Collectors.mapping(SaasProductModuleFeatureRelation::getFeatureId, Collectors.toSet()))); - } - @Override public List batchListIdentityFromPermission(List reqList) { //异步处理 @@ -693,6 +932,9 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { //不走缓存的情况:关闭缓存开关 - 缓存临时禁用 - 请求指明不走缓存 - 角色预览操作 //请求参数去重: ou-workspace req.distinctOUWorkspacePair(); + + req.setFeatureCode(featureCodeUtil.resolveFeatureCode(req.getFeatureCode())); + boolean notUseCache = !req.isUseCache() || CollectionUtil.isNotEmpty(req.getSpecifyRoleIds()) || permissionCacheService.cacheDisable( @@ -723,7 +965,21 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { Set terminals = req.getTerminal() == null ? null : new HashSet<>(req.getTerminal()); permissions.forEach(permission -> { List filterPermission = permission.getPermissionPoint().stream() - .filter(p -> CollectionUtil.isEmpty(terminals) || terminals.contains(p.getTerminal())) + .filter(p -> { + if (CollectionUtil.isEmpty(terminals)) { + return true; + } + + if (terminals.contains(p.getTerminal())) { + return true; + } + + // 历史接口会只给NT_CMS_WEB_PROJ、NT_CMS_WEB_ENT_ZB查询,但是为了兼容新老版本的权限,需要补充新版本的项目的权限code + if (terminals.contains("NT_CMS_WEB_PROJ") || terminals.contains("NT_CMS_WEB_ENT_ZB")) { + return Objects.equals(p.getTerminal(), "NT_CMS_WEB_GENERAL"); + } + return false; + }) .filter(p -> CollectionUtil.isEmpty(req.getFeatureId()) || req.getFeatureId().contains(p.getFeatureId())) .filter(p -> CollectionUtil.isEmpty(req.getFeatureCode()) || req.getFeatureCode().contains(p.getFeatureCode())) .collect(Collectors.toList()); @@ -791,7 +1047,9 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { @Override public List listAuthByResourceAndRoleGroup(ListPermissionFromRoleGroupReq listPermissionFromRoleGroupReq) { //确定角色group_id: code对应角色组及其下级 - List groups = roleGroupService.listByCodes(Collections.singletonList(listPermissionFromRoleGroupReq.getCategoryCode()), 3); + Integer categoryType = Optional.ofNullable(listPermissionFromRoleGroupReq.getCategoryType()) + .orElse(3); + List groups = roleGroupService.listByCodes(Collections.singletonList(listPermissionFromRoleGroupReq.getCategoryCode()), categoryType); if (CollectionUtil.isEmpty(groups)) { log.warn("no role group found for code :{}", listPermissionFromRoleGroupReq.getCategoryCode()); return Collections.emptyList(); @@ -805,20 +1063,25 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { if (!listPermissionFromRoleGroupReq.getFindFeatureInfo()) { return permissionInfo; } - Map> authMap = filterAuthByRoleAndProduct(permissionInfo.stream().map(e -> FilterRoleAuth.builder() + Map> authMap = filterAuthByRoleAndProduct(permissionInfo.stream().map(e -> FilterRoleAuth.builder() .roleId(NumberUtil.parseLong(e.getRoleId())) .workspaceId(e.getWorkspaceId()) .build()).collect(Collectors.toList())); - permissionInfo.forEach(e -> e.setSimpleFeatureInfos(authMap.get(NumberUtil.parseLong(e.getRoleId())))); + permissionInfo.forEach(e -> e.setFeatureInfos(authMap.get(NumberUtil.parseLong(e.getRoleId())))); + permissionInfo.forEach(e -> e.setSimpleFeatureInfos(org.apache.commons.collections4.CollectionUtils.emptyIfNull(authMap.get(NumberUtil.parseLong(e.getRoleId()))) + .stream().map(ListPermissionFromRoleGroupResp.FeatureInfo::getFeatureId).collect(Collectors.toSet()))); + return permissionInfo; } + /** * 通过工作台ID过滤指定角色的权限 + * * @param filterRoleAuths - * @return KEY :role Id ; VALUE: feature id ; + * @return KEY :role Id ; VALUE: feature id ; */ - public Map> filterAuthByRoleAndProduct(List filterRoleAuths) { + public Map> filterAuthByRoleAndProduct(List filterRoleAuths) { List roleIds = filterRoleAuths.stream().map(FilterRoleAuth::getRoleId).distinct().collect(Collectors.toList()); @@ -838,9 +1101,10 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { List productsDetail = RpcExternalUtil.rpcApiResultProcessor(() -> productFeatureRelationService.featureListByProduct(productIds), " find permission point by product ", productIds); Map>> productGroup = productsDetail.stream().collect(Collectors.groupingBy(ProductFeatureRelationVO::getProductModuleId, Collectors.groupingBy(ProductFeatureRelationVO::getDictCode))); + Map featureRelationVoMap = productsDetail.stream().collect(Collectors.toMap(ProductFeatureRelationVO::getFeatureId, Function.identity(), (v1, v2) -> v1)); // intersection auth from role and product - return filterRoleAuths.stream().collect(Collectors.toMap(FilterRoleAuth::getRoleId, e -> { + Map> map = filterRoleAuths.stream().collect(Collectors.toMap(FilterRoleAuth::getRoleId, e -> { Long roleId = e.getRoleId(); SaasRoleVO saasRole = roleMap.get(e.getRoleId()); if (null == saasRole) { @@ -877,50 +1141,104 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { oldFeatureLists.addAll(newFeatureLists); return oldFeatureLists; })); + + Map> featureMap = Maps.newHashMap(); + for (Map.Entry> entry : map.entrySet()) { + Set featureInfos = org.apache.commons.collections4.CollectionUtils.emptyIfNull(entry.getValue()).stream().map(e -> ListPermissionFromRoleGroupResp.FeatureInfo.builder() + .featureId(e) + .relationType(Optional.ofNullable(featureRelationVoMap.get(e)).map(ProductFeatureRelationVO::getType).orElse(null)) + .build()).collect(Collectors.toSet()); + featureMap.put(entry.getKey(), featureInfos); + } + + return featureMap; } + @Data @Builder @NoArgsConstructor @AllArgsConstructor public static class OUWRoleInfo { - + Long workspaceId; Integer workspaceType; Long ouId; WorkspaceJoinType workspaceJoinType; - Set roles = new HashSet<>(); + Set roles = new HashSet<>(); } @Override public List listWorkspacePermissionIdentity(WorkspacePermissionIdentityReq req) { + Set newFeatureCodes = featureCodeUtil.resolveFeatureCode(Sets.newHashSet(req.getFeatureCodes())); + req.setFeatureCodes(Lists.newArrayList(newFeatureCodes)); + //code查询权限点信息 List features = permissionPointService.listNodeWithChildrenByCodes(req.getFeatureCodes(), null); - if (CollectionUtil.isEmpty(features)) { + + // 兼容新老版本,需要通过featureCode查询新版本的features,原逻辑是查询当前菜单资源的所有子数据 + ListSaasFeatureResourceParam listSaasFeatureResourceParam = ListSaasFeatureResourceParam.builder() + .featureCodes(Sets.newHashSet(req.getFeatureCodes())) + .build(); + List saasFeatureResources = listSaasFeatureResource(listSaasFeatureResourceParam); + + if (CollectionUtil.isEmpty(features) && CollectionUtils.isEmpty(saasFeatureResources)) { log.warn("no features data found for:{}", req.getFeatureCodes()); return Collections.emptyList(); } Set featureIds = features.stream().map(SaasFeature::getId).collect(Collectors.toSet()); - //权限匹配 - 工作台是否有指定权限 productUnitType -> featureIds - Map> workspaceFeatureMap = matchWorkspaceFeature(req.getWorkspaceId(), featureIds); - if (CollectionUtil.isEmpty(workspaceFeatureMap)) { + Set newFeatureIds = saasFeatureResources.stream().map(SaasFeatureResourceResp::getId).collect(Collectors.toSet()); + List featureIdPairs = Lists.newArrayList(); + + if (!CollectionUtils.isEmpty(featureIds)) { + featureIdPairs.add(FeatureIdPair.builder().featureIds(featureIds).type(OLD_FEATURE).build()); + } + + if (!CollectionUtils.isEmpty(newFeatureIds)) { + featureIdPairs.add(FeatureIdPair.builder().featureIds(newFeatureIds).type(NEW_FEATURE).build()); + } + WorkspaceProductService.WorkspaceProductParam workspaceProductParam = WorkspaceProductService.WorkspaceProductParam.builder() + .workspaceIds(Sets.newHashSet(req.getWorkspaceId())) + .featureIdPairs(featureIdPairs) + .build(); + List workspaceProducts = workspaceProductService.listWorkspaceProduct(workspaceProductParam) + .stream() + .map(WorkspaceProductService.WorkspaceProduct::getSaasProductModuleFeatureRelations) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + + if (CollectionUtil.isEmpty(workspaceProducts)) { log.warn("no matched feature in workspace product"); return Collections.emptyList(); } //是否免授权权限点 - Set matchedFeatureIds = workspaceFeatureMap.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()); + Set matchedOldFeatureIds = workspaceProducts.stream() + .filter(e -> Objects.equals(OLD_FEATURE, e.getType())) + .map(SaasProductModuleFeatureRelation::getFeatureId) + .collect(Collectors.toSet()); Optional freeFeature = features.stream() - .filter(f -> matchedFeatureIds.contains(f.getId())) + .filter(f -> matchedOldFeatureIds.contains(f.getId())) .filter(f -> DelegatedType.NO_NEED.sameCode(f.getDelegatedType())) .findAny(); - if (freeFeature.isPresent()) { + + Set matchedNewFeatureIds = workspaceProducts.stream() + .filter(e -> Objects.equals(NEW_FEATURE, e.getType())) + .map(SaasProductModuleFeatureRelation::getFeatureId) + .collect(Collectors.toSet()); + + Optional freeFeatureResource = saasFeatureResources.stream() + .filter(f -> matchedNewFeatureIds.contains(f.getId())) + .filter(e -> SaasFeatureResource.AuthType.isAllRole(e.getAuthType())) + .findFirst(); + if (freeFeature.isPresent() || freeFeatureResource.isPresent()) { throw new ServiceException("免授权权限点调用查人接口"); } //从相关角色查询用户-超管和普通角色 - List users = getWorkspaceUser(req.getWorkspaceId(), null, workspaceFeatureMap); + List users = getWorkspaceUser(req.getWorkspaceId(), null, workspaceProducts); if (CollectionUtil.isEmpty(users)) { return Collections.emptyList(); } @@ -938,19 +1256,65 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { return result; } - private List getWorkspaceUser(Long workspaceId, Long ouId, Map> workspaceFeatureMap) { + private List listFeatureRoles(Set featureIds, Integer type) { + if (CollectionUtils.isEmpty(featureIds)) { + return Collections.emptyList(); + } - Set productTypes = workspaceFeatureMap.keySet(); - Set matchedFeatureIds = workspaceFeatureMap.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()); + List relations = saasPgroupPermissionRelationService.list(PagePgroupPermissionRelationReq.builder() + .featureIds(Lists.newArrayList(featureIds)) + .type(type) + .build()); + if (CollectionUtils.isEmpty(relations)) { + return Collections.emptyList(); + } - List matchedRoles = new ArrayList<>(); + List saasPgroupRoleRelations = saasPgroupRoleRelationDao.listByGroupIds(Lists.transform(relations, SaasPgroupPermissionRelation::getGroupId)); + + if (CollectionUtils.isEmpty(saasPgroupRoleRelations)) { + return Collections.emptyList(); + } + + return roleService.list(RoleService.ListSaasRoleParam.builder() + .roleIds(Lists.transform(saasPgroupRoleRelations, SaasPgroupRoleRelation::getRoleId)) + .build()) + .stream() + .map(e -> { + SaasRole saasRole = new SaasRole(); + BeanUtils.copyProperties(e, saasRole); + return saasRole; + }) + .collect(Collectors.toList()); + } + + private List getWorkspaceUser(Long workspaceId, Long ouId, + List workspaceProducts) { + Set newProductTypes = workspaceProducts.stream() + .filter(e -> Objects.equals(e.getType(), NEW_FEATURE)) + .map(SaasProductModuleFeatureRelation::getDictCode) + .map(Integer::valueOf) + .collect(Collectors.toSet()); + + Set newMatchedFeatureIds = workspaceProducts.stream() + .filter(e -> Objects.equals(e.getType(), NEW_FEATURE)) + .map(SaasProductModuleFeatureRelation::getFeatureId) + .collect(Collectors.toSet()); + + Set oldProductTypes = workspaceProducts.stream() + .filter(e -> Objects.equals(e.getType(), OLD_FEATURE)) + .map(SaasProductModuleFeatureRelation::getDictCode) + .map(Integer::valueOf) + .collect(Collectors.toSet()); + + Set oldMatchedFeatureIds = workspaceProducts.stream() + .filter(e -> Objects.equals(e.getType(), OLD_FEATURE)) + .map(SaasProductModuleFeatureRelation::getFeatureId) + .collect(Collectors.toSet()); //超管和管理员 List adminRoles = roleService.listAdmins(workspaceId, ouId); if (CollectionUtil.isEmpty(adminRoles)) { log.warn("no admin roles found for workspaceId:{}, ouId:{}", workspaceId, ouId); - } else { - matchedRoles.addAll(adminRoles); } Set superAdmins = adminRoles.stream() @@ -959,18 +1323,32 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { .collect(Collectors.toSet()); //普通角色 权限点查角色 -- 不考虑 角色权限集例外 - List normalRoles = roleService.queryRoleByFeatures(matchedFeatureIds); - if (CollectionUtil.isEmpty(normalRoles)) { - log.warn("no role found for featureIds:{}", matchedFeatureIds); - } else { - matchedRoles.addAll(normalRoles); - } + // 多版本只会存在一段时间,减少代码复杂度,所以查询多次 + List oldNormalSaasRoles = listFeatureRoles(oldMatchedFeatureIds, OLD_FEATURE); + List newNormalSaasRoles = listFeatureRoles(newMatchedFeatureIds, NEW_FEATURE); - //匹配角色和产品标签 - List roleIds = matchedRoles.stream() - .filter(r -> productTypes.contains(r.getProductUnitType())) + List roleIds = Lists.newArrayList(); + // 超管不用区分新老版本 + List adminRoleIds = adminRoles.stream() + .filter(r -> newProductTypes.contains(r.getProductUnitType()) || oldProductTypes.contains(r.getProductUnitType())) .map(SaasRole::getId) .collect(Collectors.toList()); + + List oldNormalRoleIds = oldNormalSaasRoles.stream() + .filter(r -> oldProductTypes.contains(r.getProductUnitType())) + .map(SaasRole::getId) + .collect(Collectors.toList()); + + List newNormalRoleIds = newNormalSaasRoles.stream() + .filter(r -> newProductTypes.contains(r.getProductUnitType())) + .map(SaasRole::getId) + .collect(Collectors.toList()); + + //匹配角色和产品标签 + roleIds.addAll(adminRoleIds); + roleIds.addAll(oldNormalRoleIds); + roleIds.addAll(newNormalRoleIds); + if (CollectionUtil.isEmpty(roleIds)) { log.warn("no role matched product unit types"); return Collections.emptyList(); @@ -1006,4 +1384,273 @@ public class TyrSaasAuthServiceImpl implements TyrSaasAuthService { return new ArrayList<>(distinctMap.values()); } + + /** + * 基于saas_feature_resource的鉴权 + * @param req + * @return + */ + @Override + public boolean authNewPermission(PermissionCheckReq req) { + ListSaasFeatureResourceParam listSaasFeatureResourceParam = ListSaasFeatureResourceParam.builder() + .featureCodes(Sets.newHashSet(req.getFeatureCodes())) + .terminal(req.getTerminal()) + .build(); + List saasFeatureResources = listSaasFeatureResource(listSaasFeatureResourceParam); + if (CollectionUtils.isEmpty(saasFeatureResources)) { + log.info("featureCode not found in featureResource:{}", req.getFeatureCodes()); + return false; + } + //用户角色关系,以及对应角色的权限点 + List saasRoleUserRelations = listRoleUserRelationsNew(req, saasFeatureResources); + if (CollectionUtils.isEmpty(saasRoleUserRelations)) { + return false; + } + + Set featureIds = saasFeatureResources.stream() + .map(SaasFeatureResourceResp::getId) + .collect(Collectors.toSet()); + + WorkspaceProductService.WorkspaceProductParam workspaceProductParam = WorkspaceProductService.WorkspaceProductParam.builder() + .workspaceIds(Sets.newHashSet(req.getWorkspaceId())) + .featureIdPairs(Lists.newArrayList( + FeatureIdPair.builder().featureIds(featureIds).type(NEW_FEATURE).build() + )) + .build(); + Set workspaceProductFeatures = workspaceProductService.listWorkspaceProductCached(workspaceProductParam).stream() + .map(WorkspaceProductService.WorkspaceProduct::getSaasProductModuleFeatureRelations) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + + if (CollectionUtils.isEmpty(workspaceProductFeatures)) { + log.info("product not found:{}", req.getWorkspaceId()); + return false; + } + + // 是否有免授权的权限码,且在租户开通了这个产品 + boolean matchedNoNeedAuthFeature = matchNoAuthFeatureNew(saasFeatureResources, workspaceProductFeatures); + if (BooleanUtil.isTrue(matchedNoNeedAuthFeature)) { + log.info("has no need auth feature:{}", req.getWorkspaceId()); + return true; + } + + // 是否有管理员角色,且租户开通了管理员角色的单位类型对应的产品权限码 + boolean matchedAdminRole = matchAdminRole(saasRoleUserRelations, workspaceProductFeatures); + if (BooleanUtil.isTrue(matchedAdminRole)) { + log.info("admin role has permission:{}", req.getWorkspaceId()); + return true; + } + return matchNormalRole(saasRoleUserRelations, workspaceProductFeatures); + } + + /** + * 基于saas_feature的鉴权,后续会去掉 + * @param req + * @return + */ + @Override + public boolean authPermission(PermissionCheckReq req) { + // saas_feature表会被废弃,所以直接查询,没提供统一的查询 + List saasFeatures = saasFeatureDao.lambdaQuery() + .in(SaasFeature::getFeatureCode, req.getFeatureCodes()) + .eq(SaasFeature::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .eq(StringUtils.isNotBlank(req.getTerminal()), SaasFeature::getTerminal, req.getTerminal()) + .list(); + if (CollectionUtils.isEmpty(saasFeatures)) { + log.info("featureCode not found in saasFeature:{}", req.getFeatureCodes()); + return false; + } + + //用户角色关系,以及对应角色的权限点 + List saasRoleUserRelations = listRoleUserRelations(req, saasFeatures); + if (CollectionUtils.isEmpty(saasRoleUserRelations)) { + return false; + } + + Set featureIds = saasFeatures.stream() + .map(SaasFeature::getId) + .collect(Collectors.toSet()); + + WorkspaceProductService.WorkspaceProductParam workspaceProductParam = WorkspaceProductService.WorkspaceProductParam.builder() + .workspaceIds(Sets.newHashSet(req.getWorkspaceId())) + .featureIdPairs(Lists.newArrayList( + FeatureIdPair.builder().featureIds(featureIds).type(OLD_FEATURE).build() + )) + .build(); + Set workspaceProductFeatures = workspaceProductService.listWorkspaceProductCached(workspaceProductParam).stream() + .map(WorkspaceProductService.WorkspaceProduct::getSaasProductModuleFeatureRelations) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + if (CollectionUtils.isEmpty(workspaceProductFeatures)) { + log.info("product not found:{}", req.getWorkspaceId()); + return false; + } + + // 是否有免授权的权限码,且在租户开通了这个产品 + boolean matchedNoNeedAuthFeature = matchNoAuthFeature(saasFeatures, workspaceProductFeatures); + if (BooleanUtil.isTrue(matchedNoNeedAuthFeature)) { + log.info("has no need auth feature:{}", req.getWorkspaceId()); + return true; + } + // 是否有管理员角色,且租户开通了管理员角色的单位类型对应的产品权限码 + boolean matchedAdminRole = matchAdminRole(saasRoleUserRelations, workspaceProductFeatures); + if (BooleanUtil.isTrue(matchedAdminRole)) { + log.info("admin role has permission:{}", req.getWorkspaceId()); + return true; + } + return matchNormalRole(saasRoleUserRelations, workspaceProductFeatures); + } + + private boolean matchNormalRole(List saasRoleUserRelations, + Set permissionProducts) { + List normalRoles = saasRoleUserRelations.stream() + .filter(e -> !RoleTypeEnum.isAdmin(e.getSaasRole().getRoleType())) + .collect(Collectors.toList()); + + if (CollectionUtils.isEmpty(normalRoles)) { + return false; + } + + // 权限点对应角色的单位类型要与权限点对应产品的单位类型一致才能算有权限 + Map> permissionProductMap = permissionProducts.stream() + .collect(Collectors.groupingBy(SaasProductModuleFeatureRelation::getFeatureId, + Collectors.mapping(SaasProductModuleFeatureRelation::getDictCode, Collectors.toList()))); + + for (SaasRoleUserV2DTO permissionRole : normalRoles) { + if (CollectionUtils.isEmpty(permissionRole.getSaasRole().getSaasPermissions())) { + continue; + } + + Set ouTypesOfProduct = permissionRole.getSaasRole().getSaasPermissions().stream() + .map(e -> permissionProductMap.get(e.getId())) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + + if (CollectionUtils.isEmpty(ouTypesOfProduct)) { + continue; + } + + if (ouTypesOfProduct.contains(String.valueOf(permissionRole.getSaasRole().getProductUnitType()))) { + return true; + } + } + + return false; + } + + private boolean matchNoAuthFeatureNew(List saasFeatureResources, + Set permissionProducts) { + + Set noNeedAuthFeatureIds = saasFeatureResources.stream() + .filter(e -> SaasFeatureResource.AuthType.isAllRole(e.getAuthType())) + .map(SaasFeatureResourceResp::getId) + .collect(Collectors.toSet()); + + if (CollectionUtils.isEmpty(noNeedAuthFeatureIds)) { + log.info("not found no need auth featureCode"); + return false; + } + + return permissionProducts.stream() + .anyMatch(e -> noNeedAuthFeatureIds.contains(e.getFeatureId())); + } + + /** + * 租户开通的产品是否有不需要鉴权的权限码 + * + * @param saasFeatures + * @param permissionProducts + * @return + */ + private boolean matchNoAuthFeature(List saasFeatures, + Set permissionProducts) { + + Set noNeedAuthFeatureIds = saasFeatures.stream() + .filter(e -> Objects.equals(e.getDelegatedType(), DelegatedType.NO_NEED.getCode())) + .map(SaasFeature::getId) + .collect(Collectors.toSet()); + + if (CollectionUtils.isEmpty(noNeedAuthFeatureIds)) { + log.info("not found no need auth featureCode"); + return false; + } + + return permissionProducts.stream() + .anyMatch(e -> noNeedAuthFeatureIds.contains(e.getFeatureId())); + } + + /** + * 匹配管理员角色是否有权限点的权限 + * + * @param saasRoleUserRelations + * @param permissionProducts + * @return + */ + private boolean matchAdminRole(List saasRoleUserRelations, + Set permissionProducts) { + + List adminRoles = saasRoleUserRelations.stream() + .filter(e -> RoleTypeEnum.isAdmin(e.getSaasRole().getRoleType())) + .collect(Collectors.toList()); + + if (CollectionUtils.isEmpty(adminRoles)) { + return false; + } + + Set ouTypes = permissionProducts.stream() + .map(SaasProductModuleFeatureRelation::getDictCode) + .collect(Collectors.toSet()); + + return adminRoles.stream() + .anyMatch(adminRole -> ouTypes.contains(String.valueOf(adminRole.getSaasRole().getProductUnitType()))); + } + + /** + * 兼容历史版本,全部切完后去掉 + * @param identityAuthReq + * @param saasFeatures + * @return + */ + private List listRoleUserRelations(PermissionCheckReq identityAuthReq, List saasFeatures) { + + List workspaceOuPairs = Lists.newArrayList( + ListRoleUserRelationParam.WorkspaceOuPair.builder() + .workspaceId(identityAuthReq.getWorkspaceId()) + .ouId(identityAuthReq.getOuId()) + .build() + ); + ListRoleUserRelationParam listRoleUserRelationParam = ListRoleUserRelationParam.builder() + .personId(identityAuthReq.getPersonId()) + .workspaceOuPairs(Lists.newArrayList(workspaceOuPairs)) + .needRole(true) + .needRolePermissionOld(true) + .featureIds(Lists.transform(saasFeatures, SaasFeature::getId)) + .build(); + return saasRoleUserRelationService.listV2(listRoleUserRelationParam).stream() + .filter(e -> e.getSaasRole() != null) + .collect(Collectors.toList()); + } + + private List listRoleUserRelationsNew(PermissionCheckReq identityAuthReq, List saasFeatureResources) { + + List workspaceOuPairs = Lists.newArrayList( + ListRoleUserRelationParam.WorkspaceOuPair.builder() + .workspaceId(identityAuthReq.getWorkspaceId()) + .ouId(identityAuthReq.getOuId()) + .build() + ); + ListRoleUserRelationParam listRoleUserRelationParam = ListRoleUserRelationParam.builder() + .personId(identityAuthReq.getPersonId()) + .workspaceOuPairs(Lists.newArrayList(workspaceOuPairs)) + .needRole(true) + .needPermission(true) + .featureIds(Lists.transform(saasFeatureResources, SaasFeatureResourceResp::getId)) + .build(); + return saasRoleUserRelationService.listV2(listRoleUserRelationParam).stream() + .filter(e -> e.getSaasRole() != null) + .collect(Collectors.toList()); + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/WorkspaceProductServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/WorkspaceProductServiceImpl.java new file mode 100644 index 00000000..a2c5ac57 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/impl/WorkspaceProductServiceImpl.java @@ -0,0 +1,290 @@ +package cn.axzo.tyr.server.service.impl; + +import cn.axzo.foundation.exception.Axssert; +import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import cn.axzo.pokonyan.config.redis.RedisUtil; +import cn.axzo.thrones.client.saas.ServicePkgClient; +import cn.axzo.thrones.client.saas.entity.serivicepgkproduct.ServicePkgProduct; +import cn.axzo.thrones.client.saas.entity.servicepkg.ServicePkgDetailRes; +import cn.axzo.tyr.server.repository.dao.ProductModuleDao; +import cn.axzo.tyr.server.repository.entity.ProductFeatureQuery; +import cn.axzo.tyr.server.repository.entity.SaasProductModuleFeatureRelation; +import cn.axzo.tyr.server.service.ProductFeatureRelationService; +import cn.axzo.tyr.server.service.WorkspaceProductService; +import cn.axzo.tyr.server.utils.RpcInternalUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.lang.Pair; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.common.collect.Streams; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.dao.DataAccessException; +import org.springframework.data.redis.core.RedisOperations; +import org.springframework.data.redis.core.SessionCallback; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static cn.axzo.tyr.server.config.exception.BizResultCode.REDIS_PRODUCT_NOT_NULL; +import static cn.axzo.tyr.server.config.exception.BizResultCode.WORKSPACE_ID_NOT_NULL; + +@Slf4j +@Service +public class WorkspaceProductServiceImpl implements WorkspaceProductService { + + @Autowired + private ServicePkgClient servicePkgClient; + @Autowired + private ProductModuleDao productModuleDao; + @Autowired + private ProductFeatureRelationService productFeatureRelationService; + @Autowired + protected StringRedisTemplate redisTemplate; + + /** 授权缓存过期时间 **/ + @Value("${workspace.product.expire:90}") + private Long expireInDays; + + private static final String WORKSPACE_PRODUCT_KEY = "workspace:product:%s"; + + @Override + public List listWorkspaceProduct(WorkspaceProductParam param) { + + if (CollectionUtils.isEmpty(param.getWorkspaceIds())) { + return Collections.emptyList(); + } + + List servicePkgDetailRes = RpcInternalUtil.rpcListProcessor(() -> servicePkgClient.getServicePkgDetailBySpaceId(param.getWorkspaceIds()), + "查询项目的产品", param.getWorkspaceIds()).getData(); + if (CollectionUtil.isEmpty(servicePkgDetailRes)) { + return Collections.emptyList(); + } + + Set productIds = servicePkgDetailRes.stream() + .map(ServicePkgDetailRes::getProducts) + .filter(CollectionUtil::isNotEmpty) + .flatMap(List::stream) + .map(ServicePkgProduct::getProductId) + .collect(Collectors.toSet()); + if (CollectionUtil.isEmpty(productIds)) { + log.warn("no product found for workspace :{}", param.getWorkspaceIds()); + return Collections.emptyList(); + } + + // 已被删除产品过滤一层 + productIds = productModuleDao.listByIds(productIds) + .stream() + .filter(productModule -> Objects.equals(productModule.getIsDelete(),0L)) + .map(BaseEntity::getId) + .collect(Collectors.toSet()); + if (CollectionUtil.isEmpty(productIds)) { + log.warn("all product is deleted for workspace :{}", param.getWorkspaceIds()); + return Collections.emptyList(); + } + + ProductFeatureQuery productFeatureQuery = ProductFeatureQuery.builder() + .productIds(productIds) + .featureResourceTypes(param.getFeatureResourceTypes()) + .terminal(param.getTerminal()) + .type(param.getType()) + .featureIdPairs(param.getFeatureIdPairs()) + .build(); + Map> saasProductModuleFeatureRelations = productFeatureRelationService.queryOnCondition(productFeatureQuery).stream() + .collect(Collectors.groupingBy(SaasProductModuleFeatureRelation::getProductModuleId)); + + return servicePkgDetailRes.stream() + .filter(e -> CollectionUtils.isNotEmpty(e.getProducts())) + .map(e -> { + List features = e.getProducts().stream() + .map(product -> saasProductModuleFeatureRelations.get(product.getProductId())) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + return Pair.of(e.getSpaceId(), features); + }) + // 考虑一个项目有多个服务包的情况 + .collect(Collectors.toMap(Pair::getKey, Pair::getValue, (f, s) -> { + f.addAll(s); + return f; + })) + .entrySet() + .stream() + .map(e -> WorkspaceProduct.builder() + .workspaceId(e.getKey()) + .saasProductModuleFeatureRelations(e.getValue()) + .build()) + .collect(Collectors.toList()); + } + + @Override + public List listWorkspaceProductCached(WorkspaceProductParam param) { + if (CollectionUtils.isEmpty(param.getWorkspaceIds())) { + return Collections.emptyList(); + } + + ListWorkspaceProductParam listWorkspaceProductParam = ListWorkspaceProductParam.builder() + .workspaceIds(param.getWorkspaceIds()) + .build(); + Map> workspaceProducts = listWorkspaceProduct(listWorkspaceProductParam); + + // 存在项目没有在缓存中查询到产品的情况 + fillCacheWorkspaseProducts(param, workspaceProducts); + + Set productIds = workspaceProducts.values().stream() + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + + if (CollectionUtils.isEmpty(productIds)) { + return Collections.emptyList(); + } + + // 已被删除产品过滤一层 + Set finalProductIds = productModuleDao.listByIds(productIds) + .stream() + .filter(productModule -> Objects.equals(productModule.getIsDelete(),0L)) + .map(BaseEntity::getId) + .collect(Collectors.toSet()); + if (CollectionUtil.isEmpty(finalProductIds)) { + log.warn("all product is deleted for workspace :{}", param.getWorkspaceIds()); + return Collections.emptyList(); + } + + ProductFeatureQuery productFeatureQuery = ProductFeatureQuery.builder() + .productIds(finalProductIds) + .featureResourceTypes(param.getFeatureResourceTypes()) + .terminal(param.getTerminal()) + .type(param.getType()) + .featureIdPairs(param.getFeatureIdPairs()) + .build(); + Map> saasProductModuleFeatureRelations = productFeatureRelationService.queryOnCondition(productFeatureQuery).stream() + .collect(Collectors.groupingBy(SaasProductModuleFeatureRelation::getProductModuleId)); + + return workspaceProducts.entrySet().stream() + .filter(e -> CollectionUtils.isNotEmpty(e.getValue())) + .map(e -> { + List features = e.getValue().stream() + .map(saasProductModuleFeatureRelations::get) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + return WorkspaceProduct.builder() + .workspaceId(e.getKey()) + .saasProductModuleFeatureRelations(features) + .build(); + }) + .collect(Collectors.toList()); + } + + @Override + public void storeWorkspaceProduct(StoreWorkspaceProductParam param) { + Axssert.checkNotEmpty(param.getWorkspaceProducts(), REDIS_PRODUCT_NOT_NULL); + + + redisTemplate.executePipelined(new SessionCallback() { + @Override + public Object execute(RedisOperations operations) throws DataAccessException { + + for (WorkspaceProductDTO workspaceProduct : param.getWorkspaceProducts()) { + String redisKey = getKey(workspaceProduct.getWorkspaceId()); + RedisUtil.StringValueOps.setEx(redisKey, JSON.toJSONString(workspaceProduct.getProductIds()), + expireInDays, TimeUnit.DAYS); + } + + return null; + } + }); + } + + private Map> listWorkspaceProduct(ListWorkspaceProductParam param) { + Axssert.checkNotEmpty(param.getWorkspaceIds(), WORKSPACE_ID_NOT_NULL); + + List redisKeys = param.getWorkspaceIds().stream() + .map(this::getKey) + .collect(Collectors.toList()); + List redisValues = redisTemplate.opsForValue().multiGet(redisKeys); + + return Streams.zip(param.getWorkspaceIds().stream(), + redisValues.stream(), + (workspaceId, redisValue) -> { + + if (StringUtils.isBlank(redisValue)) { + return null; + } + + Set productIds = JSON.parseArray(redisValue.toString()) + .stream() + .map(e -> Long.valueOf(e.toString())) + .collect(Collectors.toSet()); + + return Pair.of(workspaceId, productIds); + }) + .filter(Objects::nonNull) + .collect(Collectors.toMap(Pair::getKey, Pair::getValue)); + } + + /** + * 组装在缓存中没有查询到产品的项目 + * @param param + * @param workspaceProducts + */ + private void fillCacheWorkspaseProducts(WorkspaceProductParam param, + Map> workspaceProducts) { + + Sets.SetView difference = Sets.difference(param.getWorkspaceIds(), workspaceProducts.keySet()); + if (difference.isEmpty()) { + return; + } + + List servicePkgDetailRes = RpcInternalUtil.rpcListProcessor(() -> servicePkgClient.getServicePkgDetailBySpaceId(difference), + "查询项目的产品", difference).getData(); + if (CollectionUtil.isEmpty(servicePkgDetailRes)) { + return; + } + + List workspaceProductDTOS = Lists.newArrayList(); + + servicePkgDetailRes.forEach(e -> { + Set workpsaceProductIds = e.getProducts().stream() + .map(ServicePkgProduct::getProductId) + .collect(Collectors.toSet()); + if (CollectionUtils.isEmpty(workpsaceProductIds)) { + return; + } + + workspaceProducts.put(e.getSpaceId(), workpsaceProductIds); + + WorkspaceProductDTO workspaceProductDTO = WorkspaceProductDTO.builder() + .workspaceId(e.getSpaceId()) + .productIds(workpsaceProductIds) + .build(); + + workspaceProductDTOS.add(workspaceProductDTO); + }); + + if (CollectionUtils.isEmpty(workspaceProductDTOS)) { + return; + } + StoreWorkspaceProductParam storeWorkspaceProductParam = StoreWorkspaceProductParam.builder() + .workspaceProducts(workspaceProductDTOS) + .build(); + storeWorkspaceProduct(storeWorkspaceProductParam); + } + + private String getKey(Object... params) { + return String.format(WORKSPACE_PRODUCT_KEY, params); + } +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/util/FeatureCodeUtil.java b/tyr-server/src/main/java/cn/axzo/tyr/server/util/FeatureCodeUtil.java new file mode 100644 index 00000000..27fe4df9 --- /dev/null +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/util/FeatureCodeUtil.java @@ -0,0 +1,45 @@ +package cn.axzo.tyr.server.util; + +import cn.axzo.client.feign.FeatureCodeCachedApi; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +@Component +public class FeatureCodeUtil { + + @Autowired + private FeatureCodeCachedApi featureCodeCachedApi; + + /** + * 根据旧的featureCode找到映射的新的featureCode,并聚合返回 + * @param featureCodes + * @return + */ + public Set resolveFeatureCode(Set featureCodes) { + if (CollectionUtils.isEmpty(featureCodes)) { + return featureCodes; + } + + Map> featureCodeList = RpcInternalUtil.rpcProcessor(() -> featureCodeCachedApi.list(FeatureCodeCachedApi.ListFeatureCodeParam.builder() + .featureCodes(featureCodes) + .build()), + "featureCode list", featureCodes).getData(); + + Set newFeatureCodes = featureCodeList.values() + .stream() + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + newFeatureCodes.addAll(featureCodes); + + return newFeatureCodes; + } +} diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/util/RpcInternalUtil.java b/tyr-server/src/main/java/cn/axzo/tyr/server/util/RpcInternalUtil.java index 622bfb4c..a9671a47 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/util/RpcInternalUtil.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/util/RpcInternalUtil.java @@ -3,14 +3,18 @@ package cn.axzo.tyr.server.util; import cn.axzo.basics.common.util.AssertUtil; import cn.axzo.framework.domain.ServiceException; import cn.axzo.framework.domain.web.BizException; +import cn.axzo.framework.domain.web.result.ApiListResult; import cn.axzo.framework.domain.web.result.ApiPageResult; import cn.axzo.framework.domain.web.result.ApiResult; import cn.azxo.framework.common.model.CommonResponse; +import cn.hutool.core.date.StopWatch; import cn.hutool.core.lang.Assert; import cn.hutool.http.HttpStatus; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; +import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Supplier; @@ -100,5 +104,43 @@ public class RpcInternalUtil { return data; } + /** + * 常用的RPC请求返回值解析,如果 被请求方 返回非200会抛出异常 + */ + public static CommonResponse> rpcListProcessor(Supplier>> supplier, String operationType, Object... param) { + return rpcListProcessorMayThrow(supplier, operationType, (msg) -> { + throw new ServiceException(msg); + }, param); + } + + public static CommonResponse> rpcListProcessorMayThrow(Supplier>> supplier, String operationType, Consumer throwConsumer, Object... param) { + AssertUtil.notNull(throwConsumer, "自定义的异常处理不可为空"); + log.info(operationType + "-Param: " + JSONUtil.toJsonStr(param)); + CommonResponse> result = null; + + try { + result = printLatency(supplier, operationType); + } catch (Throwable e) { + log.warn("rpc process error:{}", e.getMessage()); + throwConsumer.accept("服务调用异常"); + } + + log.info(operationType + "-Result: " + JSONUtil.toJsonStr(result)); + Assert.notNull(result, "服务调用异常"); + // 200自定义处理 + if (HttpStatus.HTTP_OK != result.getCode()) { + throwConsumer.accept(result.getMsg()); + } + return result; + } + + public static R printLatency(Supplier function, String optType) { + StopWatch stopWatch = new StopWatch(optType); + stopWatch.start(optType); + R r = function.get(); + stopWatch.stop(); + log.info(stopWatch.shortSummary(TimeUnit.MILLISECONDS)); + return r; + } } diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/utils/RpcInternalUtil.java b/tyr-server/src/main/java/cn/axzo/tyr/server/utils/RpcInternalUtil.java index fd30b48c..edda0de6 100644 --- a/tyr-server/src/main/java/cn/axzo/tyr/server/utils/RpcInternalUtil.java +++ b/tyr-server/src/main/java/cn/axzo/tyr/server/utils/RpcInternalUtil.java @@ -3,13 +3,19 @@ package cn.axzo.tyr.server.utils; import cn.axzo.basics.common.util.AssertUtil; import cn.axzo.framework.domain.ServiceException; import cn.axzo.framework.domain.web.BizException; +import cn.axzo.framework.domain.web.result.ApiListResult; import cn.axzo.framework.domain.web.result.ApiPageResult; import cn.axzo.framework.domain.web.result.ApiResult; +import cn.axzo.thrones.client.saas.entity.servicepkg.ServicePkgDetailRes; +import cn.azxo.framework.common.model.CommonResponse; +import cn.hutool.core.date.StopWatch; import cn.hutool.core.lang.Assert; import cn.hutool.http.HttpStatus; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; +import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Supplier; @@ -87,5 +93,43 @@ public class RpcInternalUtil { return data; } + /** + * 常用的RPC请求返回值解析,如果 被请求方 返回非200会抛出异常 + */ + public static CommonResponse> rpcListProcessor(Supplier>> supplier, String operationType, Object... param) { + return rpcListProcessorMayThrow(supplier, operationType, (msg) -> { + throw new ServiceException(msg); + }, param); + } + + public static CommonResponse> rpcListProcessorMayThrow(Supplier>> supplier, String operationType, Consumer throwConsumer, Object... param) { + AssertUtil.notNull(throwConsumer, "自定义的异常处理不可为空"); + log.info(operationType + "-Param: " + JSONUtil.toJsonStr(param)); + CommonResponse> result = null; + + try { + result = printLatency(supplier, operationType); + } catch (Throwable e) { + log.warn("rpc process error:{}", e.getMessage()); + throwConsumer.accept("服务调用异常"); + } + + log.info(operationType + "-Result: " + JSONUtil.toJsonStr(result)); + Assert.notNull(result, "服务调用异常"); + // 200自定义处理 + if (HttpStatus.HTTP_OK != result.getCode()) { + throwConsumer.accept(result.getMsg()); + } + return result; + } + + public static R printLatency(Supplier function, String optType) { + StopWatch stopWatch = new StopWatch(optType); + stopWatch.start(optType); + R r = function.get(); + stopWatch.stop(); + log.info(stopWatch.shortSummary(TimeUnit.MILLISECONDS)); + return r; + } } diff --git a/tyr-server/src/main/resources/bootstrap.yml b/tyr-server/src/main/resources/bootstrap.yml index 60b3f0d7..cd3595fd 100644 --- a/tyr-server/src/main/resources/bootstrap.yml +++ b/tyr-server/src/main/resources/bootstrap.yml @@ -14,6 +14,11 @@ spring: include: swagger main: allow-bean-definition-overriding: true + lifecycle: + timeout-per-shutdown-phase: 30s + +server: + shutdown: graceful mybatis-plus: type-enums-package: cn.axzo.tyr.client.model.enums diff --git a/tyr-server/src/main/resources/mapper/SaasFeatureResourceMapper.xml b/tyr-server/src/main/resources/mapper/SaasFeatureResourceMapper.xml new file mode 100644 index 00000000..df4395bc --- /dev/null +++ b/tyr-server/src/main/resources/mapper/SaasFeatureResourceMapper.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tyr-server/src/main/resources/mapper/SaasRoleMapper.xml b/tyr-server/src/main/resources/mapper/SaasRoleMapper.xml index 283bec6a..2e417232 100644 --- a/tyr-server/src/main/resources/mapper/SaasRoleMapper.xml +++ b/tyr-server/src/main/resources/mapper/SaasRoleMapper.xml @@ -115,17 +115,6 @@ - -