Commit 16a4bc710f7f052e372df68c46bbee25107e097c
1 parent
bc33240e
迁移 树 & app模块
Showing
42 changed files
with
2023 additions
and
16 deletions
urbanops-framework/urbanops-spring-boot-starter-web/src/main/java/com/zteits/urbanops/framework/web/core/util/WebFrameworkUtils.java
| ... | ... | @@ -132,7 +132,8 @@ public class WebFrameworkUtils { |
| 132 | 132 | return UserTypeEnum.ADMIN.getValue(); |
| 133 | 133 | } |
| 134 | 134 | if (request.getServletPath().startsWith(properties.getAppApi().getPrefix())) { |
| 135 | - return UserTypeEnum.MEMBER.getValue(); | |
| 135 | + // 20251206 我改的,目前前后端用户同一套,不做区分 | |
| 136 | + return UserTypeEnum.ADMIN.getValue(); | |
| 136 | 137 | } |
| 137 | 138 | return null; |
| 138 | 139 | } | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/oldtrees/OldtreesController.java
| ... | ... | @@ -98,7 +98,8 @@ public class OldtreesController { |
| 98 | 98 | @TransMethodResult |
| 99 | 99 | public CommonResult<OldtreesRespVO> getOldtrees(@RequestParam("id") String id) { |
| 100 | 100 | OldtreesDO oldtrees = oldtreesService.getOldtrees(id); |
| 101 | - return success(BeanUtils.toBean(oldtrees, OldtreesRespVO.class)); | |
| 101 | + OldtreesRespVO bean = BeanUtils.toBean(oldtrees, OldtreesRespVO.class); | |
| 102 | + return success(bean); | |
| 102 | 103 | } |
| 103 | 104 | |
| 104 | 105 | @GetMapping({"/page","/list"}) | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/oldtrees/vo/OldtreesRespVO.java
| 1 | 1 | package com.zteits.urbanops.module.garden.controller.admin.oldtrees.vo; |
| 2 | 2 | |
| 3 | -import com.baomidou.mybatisplus.annotation.TableField; | |
| 4 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; |
| 5 | 4 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
| 6 | 5 | import com.fhs.core.trans.anno.Trans; |
| 7 | 6 | import com.fhs.core.trans.constant.TransType; |
| 7 | +import com.fhs.core.trans.vo.VO; | |
| 8 | 8 | import com.zteits.urbanops.framework.common.util.json.databind.LocalDateTimeStringSerializer; |
| 9 | 9 | import com.zteits.urbanops.module.system.dal.dataobject.dept.DeptDO; |
| 10 | 10 | import com.zteits.urbanops.module.system.dal.dataobject.user.AdminUserDO; |
| ... | ... | @@ -21,7 +21,7 @@ import static com.zteits.urbanops.framework.common.util.date.DateUtils.TIME_ZONE |
| 21 | 21 | @Schema(description = "管理后台 - 古树名木 Response VO") |
| 22 | 22 | @Data |
| 23 | 23 | @ExcelIgnoreUnannotated |
| 24 | -public class OldtreesRespVO { | |
| 24 | +public class OldtreesRespVO implements VO { | |
| 25 | 25 | |
| 26 | 26 | @Schema(description = "id", example = "4258") |
| 27 | 27 | // @ExcelProperty("id") |
| ... | ... | @@ -43,9 +43,13 @@ public class OldtreesRespVO { |
| 43 | 43 | @ExcelProperty("所在地") |
| 44 | 44 | private String location; |
| 45 | 45 | |
| 46 | + @Trans(type = TransType.SIMPLE, target = DeptDO.class, fields = "name",ref = "maintainunitName") | |
| 47 | + private String maintainunit; | |
| 48 | + | |
| 46 | 49 | @Schema(description = "养护单位") |
| 47 | 50 | @ExcelProperty("养护单位") |
| 48 | - private String maintainunit; | |
| 51 | + private String maintainunitName; | |
| 52 | + | |
| 49 | 53 | |
| 50 | 54 | @Schema(description = "权属分类--数据字典:专业绿地和单位(居住区)") |
| 51 | 55 | @ExcelProperty("权属分类") | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/oldtreeschangelog/vo/OldtreesChangeLogRespVO.java
| ... | ... | @@ -47,10 +47,12 @@ public class OldtreesChangeLogRespVO implements VO { |
| 47 | 47 | @ExcelProperty("所在地") |
| 48 | 48 | private String location; |
| 49 | 49 | |
| 50 | + @Trans(type = TransType.SIMPLE, target = DeptDO.class, fields = "name",ref = "maintainunitName") | |
| 51 | + private String maintainunit; | |
| 52 | + | |
| 50 | 53 | @Schema(description = "养护单位") |
| 51 | 54 | @ExcelProperty("养护单位") |
| 52 | - @Trans(type = TransType.SIMPLE, target = DeptDO.class, fields = "name",ref = "maintainunit") | |
| 53 | - private String maintainunit; | |
| 55 | + private String maintainunitName; | |
| 54 | 56 | |
| 55 | 57 | @Schema(description = "权属分类--数据字典:专业绿地和单位(居住区)") |
| 56 | 58 | @ExcelProperty("权属分类--数据字典:专业绿地和单位(居住区)") | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/tree/vo/TreeRespVO.java
| ... | ... | @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; |
| 5 | 5 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
| 6 | 6 | import com.fhs.core.trans.anno.Trans; |
| 7 | 7 | import com.fhs.core.trans.constant.TransType; |
| 8 | +import com.fhs.core.trans.vo.VO; | |
| 8 | 9 | import com.zteits.urbanops.framework.common.util.json.databind.LocalDateTimeStringSerializer; |
| 9 | 10 | import com.zteits.urbanops.module.system.dal.dataobject.dept.DeptDO; |
| 10 | 11 | import com.zteits.urbanops.module.system.dal.dataobject.user.AdminUserDO; |
| ... | ... | @@ -21,7 +22,7 @@ import static com.zteits.urbanops.framework.common.util.date.DateUtils.TIME_ZONE |
| 21 | 22 | @Schema(description = "管理后台 - 一树一档案 Response VO") |
| 22 | 23 | @Data |
| 23 | 24 | @ExcelIgnoreUnannotated |
| 24 | -public class TreeRespVO { | |
| 25 | +public class TreeRespVO implements VO { | |
| 25 | 26 | |
| 26 | 27 | @Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "28236") |
| 27 | 28 | // @ExcelProperty("id") |
| ... | ... | @@ -71,9 +72,13 @@ public class TreeRespVO { |
| 71 | 72 | // @ExcelProperty("所在地") |
| 72 | 73 | private String location; |
| 73 | 74 | |
| 75 | + @Trans(type = TransType.SIMPLE, target = DeptDO.class, fields = "name", ref = "maintainunitName") | |
| 76 | + private String maintainunit; | |
| 77 | + | |
| 74 | 78 | @Schema(description = "养护单位") |
| 75 | 79 | @ExcelProperty("养护单位") |
| 76 | - private String maintainunit; | |
| 80 | + private String maintainunitName; | |
| 81 | + | |
| 77 | 82 | |
| 78 | 83 | @Schema(description = "管护单位") |
| 79 | 84 | @ExcelProperty("管护单位") | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/treechangelog/vo/TreeChangeLogRespVO.java
| ... | ... | @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; |
| 4 | 4 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
| 5 | 5 | import com.fhs.core.trans.anno.Trans; |
| 6 | 6 | import com.fhs.core.trans.constant.TransType; |
| 7 | +import com.fhs.core.trans.vo.VO; | |
| 7 | 8 | import com.zteits.urbanops.framework.common.util.json.databind.LocalDateTimeStringSerializer; |
| 8 | 9 | import com.zteits.urbanops.module.system.dal.dataobject.dept.DeptDO; |
| 9 | 10 | import com.zteits.urbanops.module.system.dal.dataobject.user.AdminUserDO; |
| ... | ... | @@ -20,7 +21,7 @@ import static com.zteits.urbanops.framework.common.util.date.DateUtils.TIME_ZONE |
| 20 | 21 | @Schema(description = "管理后台 - 一树一档案修改记录 Response VO") |
| 21 | 22 | @Data |
| 22 | 23 | @ExcelIgnoreUnannotated |
| 23 | -public class TreeChangeLogRespVO { | |
| 24 | +public class TreeChangeLogRespVO implements VO { | |
| 24 | 25 | |
| 25 | 26 | @Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "3528") |
| 26 | 27 | @ExcelProperty("id") |
| ... | ... | @@ -74,10 +75,12 @@ public class TreeChangeLogRespVO { |
| 74 | 75 | @ExcelProperty("所在地") |
| 75 | 76 | private String location; |
| 76 | 77 | |
| 78 | + @Trans(type = TransType.SIMPLE, target = DeptDO.class, fields = "name",ref = "maintainunitName") | |
| 79 | + private String maintainunit; | |
| 80 | + | |
| 77 | 81 | @Schema(description = "养护单位") |
| 78 | 82 | @ExcelProperty("养护单位") |
| 79 | - @Trans(type = TransType.SIMPLE, target = DeptDO.class, fields = "name",ref = "maintainunit") | |
| 80 | - private String maintainunit; | |
| 83 | + private String maintainunitName; | |
| 81 | 84 | |
| 82 | 85 | @Schema(description = "管护单位") |
| 83 | 86 | @ExcelProperty("管护单位") | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/dataobject/oldtrees/OldtreesDO.java
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/dataobject/oldtreeschangelog/OldtreesChangeLogDO.java
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/dataobject/tree/TreeDO.java
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/dataobject/treechangelog/TreeChangeLogDO.java
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/oldtrees/OldtreesMapper.java
| ... | ... | @@ -22,7 +22,7 @@ public interface OldtreesMapper extends BaseMapperX<OldtreesDO> { |
| 22 | 22 | |
| 23 | 23 | default PageResult<OldtreesDO> selectPage(OldtreesPageReqVO reqVO) { |
| 24 | 24 | MPJLambdaWrapperX<OldtreesDO> wrapper = new MPJLambdaWrapperX<OldtreesDO>() |
| 25 | - .selectAs(DeptDO::getName, OldtreesDO::getMaintainunit) | |
| 25 | + .selectAs(DeptDO::getName, OldtreesDO::getMaintainunitName) | |
| 26 | 26 | .selectAll(OldtreesDO.class) |
| 27 | 27 | .leftJoin(DeptDO.class, DeptDO::getId, OldtreesDO::getMaintainunit) |
| 28 | 28 | .eqIfPresent(OldtreesDO::getFamoustreenumber, reqVO.getFamoustreenumber()) |
| ... | ... | @@ -66,6 +66,7 @@ public interface OldtreesMapper extends BaseMapperX<OldtreesDO> { |
| 66 | 66 | .eqIfPresent(OldtreesDO::getUpdateby, reqVO.getUpdateby()) |
| 67 | 67 | .betweenIfPresent(OldtreesDO::getUpdatetime, reqVO.getUpdatetime()) |
| 68 | 68 | .eqIfPresent(OldtreesDO::getDeptId, reqVO.getDeptId()) |
| 69 | + .orderByDesc(OldtreesDO::getUpdatetime) | |
| 69 | 70 | .orderByDesc(OldtreesDO::getCreatetime); |
| 70 | 71 | |
| 71 | 72 | return selectJoinPage(reqVO, OldtreesDO.class, wrapper); | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/oldtreeschangelog/OldtreesChangeLogMapper.java
| ... | ... | @@ -22,7 +22,7 @@ public interface OldtreesChangeLogMapper extends BaseMapperX<OldtreesChangeLogDO |
| 22 | 22 | default PageResult<OldtreesChangeLogDO> selectPage(OldtreesChangeLogPageReqVO reqVO) { |
| 23 | 23 | |
| 24 | 24 | MPJLambdaWrapperX<OldtreesChangeLogDO> wrapper = new MPJLambdaWrapperX<OldtreesChangeLogDO>() |
| 25 | - .selectAs(DeptDO::getName, OldtreesChangeLogDO::getMaintainunit) | |
| 25 | + .selectAs(DeptDO::getName, OldtreesChangeLogDO::getMaintainunitName) | |
| 26 | 26 | .selectAll(OldtreesChangeLogDO.class) |
| 27 | 27 | .leftJoin(DeptDO.class, DeptDO::getId, OldtreesChangeLogDO::getMaintainunit) |
| 28 | 28 | .eqIfPresent(OldtreesChangeLogDO::getOldtreeid, reqVO.getOldtreeid()) |
| ... | ... | @@ -66,6 +66,7 @@ public interface OldtreesChangeLogMapper extends BaseMapperX<OldtreesChangeLogDO |
| 66 | 66 | .betweenIfPresent(OldtreesChangeLogDO::getCreatetime, reqVO.getCreatetime()) |
| 67 | 67 | .eqIfPresent(OldtreesChangeLogDO::getUpdateby, reqVO.getUpdateby()) |
| 68 | 68 | .betweenIfPresent(OldtreesChangeLogDO::getUpdatetime, reqVO.getUpdatetime()) |
| 69 | + .orderByDesc(OldtreesChangeLogDO::getUpdatetime) | |
| 69 | 70 | .orderByDesc(OldtreesChangeLogDO::getCreatetime); |
| 70 | 71 | |
| 71 | 72 | return selectJoinPage(reqVO, OldtreesChangeLogDO.class, wrapper); | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/tree/TreeMapper.java
| ... | ... | @@ -22,7 +22,7 @@ public interface TreeMapper extends BaseMapperX<TreeDO> { |
| 22 | 22 | default PageResult<TreeDO> selectPage(TreePageReqVO reqVO) { |
| 23 | 23 | |
| 24 | 24 | MPJLambdaWrapperX<TreeDO> wrapper = new MPJLambdaWrapperX<TreeDO>() |
| 25 | - .selectAs(DeptDO::getName, TreeDO::getMaintainunit) | |
| 25 | + .selectAs(DeptDO::getName, TreeDO::getMaintainunitName) | |
| 26 | 26 | .selectAll(TreeDO.class) |
| 27 | 27 | .leftJoin(DeptDO.class, DeptDO::getId, TreeDO::getMaintainunit) |
| 28 | 28 | .eqIfPresent(TreeDO::getTreenumber, reqVO.getTreenumber()) |
| ... | ... | @@ -58,6 +58,7 @@ public interface TreeMapper extends BaseMapperX<TreeDO> { |
| 58 | 58 | .betweenIfPresent(TreeDO::getUpdatetime, reqVO.getUpdatetime()) |
| 59 | 59 | .eqIfPresent(TreeDO::getRemark, reqVO.getRemark()) |
| 60 | 60 | .eqIfPresent(TreeDO::getDeptId, reqVO.getDeptId()) |
| 61 | + .orderByDesc(TreeDO::getUpdatetime) | |
| 61 | 62 | .orderByDesc(TreeDO::getId); |
| 62 | 63 | |
| 63 | 64 | return selectJoinPage(reqVO, TreeDO.class, wrapper); | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/treechangelog/TreeChangeLogMapper.java
| ... | ... | @@ -22,7 +22,7 @@ public interface TreeChangeLogMapper extends BaseMapperX<TreeChangeLogDO> { |
| 22 | 22 | default PageResult<TreeChangeLogDO> selectPage(TreeChangeLogPageReqVO reqVO) { |
| 23 | 23 | |
| 24 | 24 | MPJLambdaWrapperX<TreeChangeLogDO> wrapper = new MPJLambdaWrapperX<TreeChangeLogDO>() |
| 25 | - .selectAs(DeptDO::getName, TreeChangeLogDO::getMaintainunit) | |
| 25 | + .selectAs(DeptDO::getName, TreeChangeLogDO::getMaintainunitName) | |
| 26 | 26 | .selectAll(TreeChangeLogDO.class) |
| 27 | 27 | .leftJoin(DeptDO.class, DeptDO::getId, TreeChangeLogDO::getMaintainunit) |
| 28 | 28 | .eqIfPresent(TreeChangeLogDO::getTreeid, reqVO.getTreeid()) |
| ... | ... | @@ -59,6 +59,7 @@ public interface TreeChangeLogMapper extends BaseMapperX<TreeChangeLogDO> { |
| 59 | 59 | .eqIfPresent(TreeChangeLogDO::getUpdateby, reqVO.getUpdateby()) |
| 60 | 60 | .betweenIfPresent(TreeChangeLogDO::getUpdatetime, reqVO.getUpdatetime()) |
| 61 | 61 | .eqIfPresent(TreeChangeLogDO::getRemark, reqVO.getRemark()) |
| 62 | + .orderByDesc(TreeChangeLogDO::getUpdatetime) | |
| 62 | 63 | .orderByDesc(TreeChangeLogDO::getId); |
| 63 | 64 | |
| 64 | 65 | return selectJoinPage(reqVO, TreeChangeLogDO.class, wrapper); | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/controller/admin/appmodule/AppModuleController.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.controller.admin.appmodule; | |
| 2 | + | |
| 3 | +import com.zteits.urbanops.framework.common.enums.CommonStatusEnum; | |
| 4 | +import com.zteits.urbanops.framework.common.pojo.CommonResult; | |
| 5 | +import com.zteits.urbanops.module.system.controller.admin.appmodule.vo.AppModuleListReqVO; | |
| 6 | +import com.zteits.urbanops.module.system.controller.admin.appmodule.vo.AppModuleRespVO; | |
| 7 | +import com.zteits.urbanops.module.system.controller.admin.appmodule.vo.AppModuleSaveVO; | |
| 8 | +import com.zteits.urbanops.module.system.controller.admin.appmodule.vo.AppModuleSimpleRespVO; | |
| 9 | +import com.zteits.urbanops.module.system.convert.appmodule.AppModuleConvert; | |
| 10 | +import com.zteits.urbanops.module.system.dal.dataobject.appmodule.AppModuleDO; | |
| 11 | +import com.zteits.urbanops.module.system.service.appmodule.AppModuleService; | |
| 12 | +import io.swagger.v3.oas.annotations.Operation; | |
| 13 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 14 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 15 | +import jakarta.annotation.Resource; | |
| 16 | +import jakarta.validation.Valid; | |
| 17 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 18 | +import org.springframework.validation.annotation.Validated; | |
| 19 | +import org.springframework.web.bind.annotation.*; | |
| 20 | + | |
| 21 | +import java.util.Comparator; | |
| 22 | +import java.util.List; | |
| 23 | + | |
| 24 | +import static com.zteits.urbanops.framework.common.pojo.CommonResult.success; | |
| 25 | + | |
| 26 | + | |
| 27 | +/** | |
| 28 | + * 管理后台 - App 功能模块 | |
| 29 | + * | |
| 30 | + * @author system | |
| 31 | + */ | |
| 32 | +@Tag(name = "管理后台 - App 功能模块") | |
| 33 | +@RestController | |
| 34 | +@RequestMapping("/system/app-module") | |
| 35 | +@Validated | |
| 36 | +public class AppModuleController { | |
| 37 | + | |
| 38 | + @Resource | |
| 39 | + private AppModuleService appModuleService; | |
| 40 | + | |
| 41 | + @PostMapping("/create") | |
| 42 | + @Operation(summary = "创建 App 功能模块") | |
| 43 | + @PreAuthorize("@ss.hasPermission('system:app-module:create')") | |
| 44 | + public CommonResult<Long> createAppModule(@Valid @RequestBody AppModuleSaveVO createReqVO) { | |
| 45 | + Long moduleId = appModuleService.createAppModule(createReqVO); | |
| 46 | + return success(moduleId); | |
| 47 | + } | |
| 48 | + | |
| 49 | + @PutMapping("/update") | |
| 50 | + @Operation(summary = "修改 App 功能模块") | |
| 51 | + @PreAuthorize("@ss.hasPermission('system:app-module:update')") | |
| 52 | + public CommonResult<Boolean> updateAppModule(@Valid @RequestBody AppModuleSaveVO updateReqVO) { | |
| 53 | + appModuleService.updateAppModule(updateReqVO); | |
| 54 | + return success(true); | |
| 55 | + } | |
| 56 | + | |
| 57 | + @DeleteMapping("/delete") | |
| 58 | + @Operation(summary = "删除 App 功能模块") | |
| 59 | + @Parameter(name = "id", description = "模块编号", required = true, example = "1024") | |
| 60 | + @PreAuthorize("@ss.hasPermission('system:app-module:delete')") | |
| 61 | + public CommonResult<Boolean> deleteAppModule(@RequestParam("id") Long id) { | |
| 62 | + appModuleService.deleteAppModule(id); | |
| 63 | + return success(true); | |
| 64 | + } | |
| 65 | + | |
| 66 | + @DeleteMapping("/delete-list") | |
| 67 | + @Operation(summary = "批量删除 App 功能模块") | |
| 68 | + @Parameter(name = "ids", description = "编号列表", required = true) | |
| 69 | + @PreAuthorize("@ss.hasPermission('system:app-module:delete')") | |
| 70 | + public CommonResult<Boolean> deleteAppModuleList(@RequestParam("ids") List<Long> ids) { | |
| 71 | + appModuleService.deleteAppModuleList(ids); | |
| 72 | + return success(true); | |
| 73 | + } | |
| 74 | + | |
| 75 | + @GetMapping("/list") | |
| 76 | + @Operation(summary = "获取 App 功能模块列表", description = "用于【App 功能模块管理】界面") | |
| 77 | + @PreAuthorize("@ss.hasPermission('system:app-module:query')") | |
| 78 | + public CommonResult<List<AppModuleRespVO>> getAppModuleList(AppModuleListReqVO reqVO) { | |
| 79 | + List<AppModuleDO> list = appModuleService.getAppModuleList(reqVO); | |
| 80 | + list.sort(Comparator.comparing(AppModuleDO::getSort)); | |
| 81 | + return success(AppModuleConvert.INSTANCE.convertList(list)); | |
| 82 | + } | |
| 83 | + | |
| 84 | + @GetMapping("/list-all-simple") | |
| 85 | + @Operation(summary = "获取 App 功能模块精简信息列表", | |
| 86 | + description = "只包含被开启的模块,用于配置、选择等功能") | |
| 87 | + public CommonResult<List<AppModuleSimpleRespVO>> getSimpleAppModuleList() { | |
| 88 | + AppModuleListReqVO reqVO = new AppModuleListReqVO(); | |
| 89 | + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 90 | + List<AppModuleDO> list = appModuleService.getAppModuleList(reqVO); | |
| 91 | + list = appModuleService.filterDisableModules(list); | |
| 92 | + list.sort(Comparator.comparing(AppModuleDO::getSort)); | |
| 93 | + return success(AppModuleConvert.INSTANCE.convertSimpleList(list)); | |
| 94 | + } | |
| 95 | + | |
| 96 | + @GetMapping("/get") | |
| 97 | + @Operation(summary = "获取 App 功能模块信息") | |
| 98 | + @Parameter(name = "id", description = "模块编号", required = true, example = "1024") | |
| 99 | + @PreAuthorize("@ss.hasPermission('system:app-module:query')") | |
| 100 | + public CommonResult<AppModuleRespVO> getAppModule(@RequestParam("id") Long id) { | |
| 101 | + AppModuleDO module = appModuleService.getAppModule(id); | |
| 102 | + return success(AppModuleConvert.INSTANCE.convert(module)); | |
| 103 | + } | |
| 104 | + | |
| 105 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/controller/admin/appmodule/vo/AppModuleListReqVO.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.controller.admin.appmodule.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - App 功能模块列表 Request VO") | |
| 7 | +@Data | |
| 8 | +public class AppModuleListReqVO { | |
| 9 | + | |
| 10 | + @Schema(description = "模块名称,模糊匹配", example = "个人") | |
| 11 | + private String name; | |
| 12 | + | |
| 13 | + @Schema(description = "状态,参见 CommonStatusEnum 枚举类", example = "0") | |
| 14 | + private Integer status; | |
| 15 | + | |
| 16 | + @Schema(description = "模块类型,参见 AppModuleTypeEnum 枚举类", example = "2") | |
| 17 | + private Integer type; | |
| 18 | + | |
| 19 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/controller/admin/appmodule/vo/AppModuleRespVO.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.controller.admin.appmodule.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.time.LocalDateTime; | |
| 7 | +import java.util.List; | |
| 8 | +import java.util.Map; | |
| 9 | + | |
| 10 | +@Schema(description = "管理后台 - App 功能模块信息 Response VO") | |
| 11 | +@Data | |
| 12 | +public class AppModuleRespVO { | |
| 13 | + | |
| 14 | + @Schema(description = "模块编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 15 | + private Long id; | |
| 16 | + | |
| 17 | + @Schema(description = "模块名称(主标题)", requiredMode = Schema.RequiredMode.REQUIRED, example = "个人中心") | |
| 18 | + private String name; | |
| 19 | + | |
| 20 | + @Schema(description = "模块副标题", example = "查看个人信息") | |
| 21 | + private String subtitle; | |
| 22 | + | |
| 23 | + @Schema(description = "类型,参见 AppModuleTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 24 | + private Integer type; | |
| 25 | + | |
| 26 | + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 27 | + private Integer sort; | |
| 28 | + | |
| 29 | + @Schema(description = "父模块 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | |
| 30 | + private Long parentId; | |
| 31 | + | |
| 32 | + @Schema(description = "模块图标 URL", example = "https://example.com/icon.png") | |
| 33 | + private String icon; | |
| 34 | + | |
| 35 | + @Schema(description = "跳转路径", example = "/user/profile") | |
| 36 | + private String jumpUrl; | |
| 37 | + | |
| 38 | + @Schema(description = "小程序 AppId", example = "wx1234567890") | |
| 39 | + private String miniAppId; | |
| 40 | + | |
| 41 | + @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | |
| 42 | + private Integer status; | |
| 43 | + | |
| 44 | + @Schema(description = "是否可见", example = "true") | |
| 45 | + private Boolean visible; | |
| 46 | + | |
| 47 | + @Schema(description = "可见范围,见 AppModuleVisibleScopeEnum 枚举", example = "1") | |
| 48 | + private Integer visibleScope; | |
| 49 | + | |
| 50 | + @Schema(description = "可见范围配置 JSON", example = "{}") | |
| 51 | + private List<?> visibleConfig; | |
| 52 | + | |
| 53 | + @Schema(description = "是否支持灰度发布", example = "false") | |
| 54 | + private Boolean grayRelease; | |
| 55 | + | |
| 56 | + @Schema(description = "灰度发布配置 JSON", example = "{}") | |
| 57 | + private Map<String, Object> grayConfig; | |
| 58 | + | |
| 59 | + @Schema(description = "角标文本", example = "NEW") | |
| 60 | + private String badgeText; | |
| 61 | + | |
| 62 | + @Schema(description = "角标颜色", example = "#FF0000") | |
| 63 | + private String badgeColor; | |
| 64 | + | |
| 65 | + @Schema(description = "扩展字段 JSON", example = "{}") | |
| 66 | + private Map<String, Object> extra; | |
| 67 | + | |
| 68 | + @Schema(description = "备注", example = "这是一个测试模块") | |
| 69 | + private String remark; | |
| 70 | + | |
| 71 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 72 | + private LocalDateTime createTime; | |
| 73 | + | |
| 74 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/controller/admin/appmodule/vo/AppModuleSaveVO.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.controller.admin.appmodule.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import jakarta.validation.constraints.NotBlank; | |
| 5 | +import jakarta.validation.constraints.NotNull; | |
| 6 | +import jakarta.validation.constraints.Size; | |
| 7 | +import lombok.Data; | |
| 8 | + | |
| 9 | +import java.util.List; | |
| 10 | +import java.util.Map; | |
| 11 | + | |
| 12 | +@Schema(description = "管理后台 - App 功能模块创建/修改 Request VO") | |
| 13 | +@Data | |
| 14 | +public class AppModuleSaveVO { | |
| 15 | + | |
| 16 | + @Schema(description = "模块编号", example = "1024") | |
| 17 | + private Long id; | |
| 18 | + | |
| 19 | + @Schema(description = "模块名称(主标题)", requiredMode = Schema.RequiredMode.REQUIRED, example = "个人中心") | |
| 20 | + @NotBlank(message = "模块名称不能为空") | |
| 21 | + @Size(max = 50, message = "模块名称长度不能超过50个字符") | |
| 22 | + private String name; | |
| 23 | + | |
| 24 | + @Schema(description = "模块副标题", example = "查看个人信息") | |
| 25 | + @Size(max = 100, message = "副标题长度不能超过100个字符") | |
| 26 | + private String subtitle; | |
| 27 | + | |
| 28 | + @Schema(description = "类型,参见 AppModuleTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 29 | + @NotNull(message = "模块类型不能为空") | |
| 30 | + private Integer type; | |
| 31 | + | |
| 32 | + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 33 | + @NotNull(message = "显示顺序不能为空") | |
| 34 | + private Integer sort; | |
| 35 | + | |
| 36 | + @Schema(description = "父模块 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | |
| 37 | + @NotNull(message = "父模块 ID 不能为空") | |
| 38 | + private Long parentId; | |
| 39 | + | |
| 40 | + @Schema(description = "模块图标 URL", example = "https://example.com/icon.png") | |
| 41 | + @Size(max = 500, message = "图标 URL 不能超过500个字符") | |
| 42 | + private String icon; | |
| 43 | + | |
| 44 | + @Schema(description = "跳转路径", example = "/user/profile") | |
| 45 | + @Size(max = 500, message = "跳转路径不能超过500个字符") | |
| 46 | + private String jumpUrl; | |
| 47 | + | |
| 48 | + @Schema(description = "小程序 AppId", example = "wx1234567890") | |
| 49 | + @Size(max = 100, message = "小程序 AppId 不能超过100个字符") | |
| 50 | + private String miniAppId; | |
| 51 | + | |
| 52 | + @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | |
| 53 | + @NotNull(message = "状态不能为空") | |
| 54 | + private Integer status; | |
| 55 | + | |
| 56 | + @Schema(description = "是否可见", example = "true") | |
| 57 | + private Boolean visible; | |
| 58 | + | |
| 59 | + @Schema(description = "可见范围,见 AppModuleVisibleScopeEnum 枚举", example = "1") | |
| 60 | + private Integer visibleScope; | |
| 61 | + | |
| 62 | + @Schema(description = "可见范围配置 JSON", example = "{}") | |
| 63 | + @Size(max = 2000, message = "可见范围配置不能超过2000个字符") | |
| 64 | + private List<?> visibleConfig; | |
| 65 | + | |
| 66 | + @Schema(description = "是否支持灰度发布", example = "false") | |
| 67 | + private Boolean grayRelease; | |
| 68 | + | |
| 69 | + @Schema(description = "灰度发布配置 JSON", example = "{}") | |
| 70 | + private Map<String, Object> grayConfig; | |
| 71 | + | |
| 72 | + @Schema(description = "角标文本", example = "NEW") | |
| 73 | + @Size(max = 20, message = "角标文本不能超过20个字符") | |
| 74 | + private String badgeText; | |
| 75 | + | |
| 76 | + @Schema(description = "角标颜色", example = "#FF0000") | |
| 77 | + @Size(max = 20, message = "角标颜色不能超过20个字符") | |
| 78 | + private String badgeColor; | |
| 79 | + | |
| 80 | + @Schema(description = "扩展字段 JSON", example = "{}") | |
| 81 | + private Map<String, Object> extra; | |
| 82 | + | |
| 83 | + @Schema(description = "备注", example = "这是一个测试模块") | |
| 84 | + @Size(max = 500, message = "备注不能超过500个字符") | |
| 85 | + private String remark; | |
| 86 | + | |
| 87 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/controller/admin/appmodule/vo/AppModuleSimpleRespVO.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.controller.admin.appmodule.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - App 功能模块精简 Response VO") | |
| 7 | +@Data | |
| 8 | +public class AppModuleSimpleRespVO { | |
| 9 | + | |
| 10 | + @Schema(description = "模块编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 11 | + private Long id; | |
| 12 | + | |
| 13 | + @Schema(description = "模块名称(主标题)", requiredMode = Schema.RequiredMode.REQUIRED, example = "个人中心") | |
| 14 | + private String name; | |
| 15 | + | |
| 16 | + @Schema(description = "父模块 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | |
| 17 | + private Long parentId; | |
| 18 | + | |
| 19 | + @Schema(description = "类型,参见 AppModuleTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 20 | + private Integer type; | |
| 21 | + | |
| 22 | + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 23 | + private Integer sort; | |
| 24 | + | |
| 25 | + @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | |
| 26 | + private Integer status; | |
| 27 | + | |
| 28 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/controller/app/appmodule/AppAppModuleController.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.controller.app.appmodule; | |
| 2 | + | |
| 3 | +import com.zteits.urbanops.framework.common.pojo.CommonResult; | |
| 4 | +import com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils; | |
| 5 | +import com.zteits.urbanops.module.system.controller.admin.appmodule.vo.AppModuleListReqVO; | |
| 6 | +import com.zteits.urbanops.module.system.controller.app.appmodule.vo.AppModuleVO; | |
| 7 | +import com.zteits.urbanops.module.system.convert.appmodule.AppModuleConvert; | |
| 8 | +import com.zteits.urbanops.module.system.dal.dataobject.appmodule.AppModuleDO; | |
| 9 | +import com.zteits.urbanops.module.system.service.appmodule.AppModuleService; | |
| 10 | +import io.swagger.v3.oas.annotations.Operation; | |
| 11 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 12 | +import jakarta.annotation.Resource; | |
| 13 | +import org.springframework.validation.annotation.Validated; | |
| 14 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 15 | +import org.springframework.web.bind.annotation.RequestMapping; | |
| 16 | +import org.springframework.web.bind.annotation.RestController; | |
| 17 | + | |
| 18 | +import java.util.Comparator; | |
| 19 | +import java.util.List; | |
| 20 | + | |
| 21 | +import static com.zteits.urbanops.framework.common.pojo.CommonResult.success; | |
| 22 | + | |
| 23 | + | |
| 24 | +/** | |
| 25 | + * 用户 APP - App 功能模块 | |
| 26 | + * | |
| 27 | + * @author system | |
| 28 | + */ | |
| 29 | +@Tag(name = "用户 APP - App 功能模块") | |
| 30 | +@RestController | |
| 31 | +@RequestMapping("/member/app-module") | |
| 32 | +@Validated | |
| 33 | +public class AppAppModuleController { | |
| 34 | + | |
| 35 | + @Resource | |
| 36 | + private AppModuleService appModuleService; | |
| 37 | + | |
| 38 | + @GetMapping("/list") | |
| 39 | + @Operation(summary = "获取 App 功能模块列表", description = "获取用户可见的功能模块列表") | |
| 40 | + public CommonResult<List<AppModuleVO>> getAppModuleList(AppModuleListReqVO reqVO) { | |
| 41 | + // 获取当前登录用户ID | |
| 42 | + Long userId = SecurityFrameworkUtils.getLoginUserId(); | |
| 43 | + | |
| 44 | + // 查询用户可见的模块列表 | |
| 45 | + List<AppModuleDO> list = appModuleService.getVisibleAppModuleList(userId, reqVO); | |
| 46 | + | |
| 47 | + // 填充统计数据(根据extra中的statisticKey配置) | |
| 48 | +// appModuleService.fillModuleStatistics(list, userId); | |
| 49 | + | |
| 50 | + // 排序并转换 | |
| 51 | + list.sort(Comparator.comparing(AppModuleDO::getSort)); | |
| 52 | + List<AppModuleVO> result = AppModuleConvert.INSTANCE.convertAppList(list); | |
| 53 | + | |
| 54 | + // MapStruct 转换已通过 @AfterMapping 自动填充 statisticCount | |
| 55 | + | |
| 56 | + return success(result); | |
| 57 | + } | |
| 58 | + | |
| 59 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/controller/app/appmodule/vo/AppModuleVO.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.controller.app.appmodule.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.util.Map; | |
| 7 | + | |
| 8 | +@Schema(description = "用户 APP - App 功能模块 Response VO") | |
| 9 | +@Data | |
| 10 | +public class AppModuleVO { | |
| 11 | + | |
| 12 | + @Schema(description = "模块编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 13 | + private Long id; | |
| 14 | + | |
| 15 | + @Schema(description = "模块名称(主标题)", requiredMode = Schema.RequiredMode.REQUIRED, example = "个人中心") | |
| 16 | + private String name; | |
| 17 | + | |
| 18 | + @Schema(description = "模块副标题", example = "查看个人信息") | |
| 19 | + private String subtitle; | |
| 20 | + | |
| 21 | + @Schema(description = "类型,1-目录 2-原生页面 3-H5页面 4-小程序 5-外部链接", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | |
| 22 | + private Integer type; | |
| 23 | + | |
| 24 | + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 25 | + private Integer sort; | |
| 26 | + | |
| 27 | + @Schema(description = "父模块 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | |
| 28 | + private Long parentId; | |
| 29 | + | |
| 30 | + @Schema(description = "模块图标 URL", example = "https://example.com/icon.png") | |
| 31 | + private String icon; | |
| 32 | + | |
| 33 | + @Schema(description = "跳转路径", example = "/user/profile") | |
| 34 | + private String jumpUrl; | |
| 35 | + | |
| 36 | + @Schema(description = "小程序 AppId", example = "wx1234567890") | |
| 37 | + private String miniAppId; | |
| 38 | + | |
| 39 | + @Schema(description = "角标文本", example = "NEW") | |
| 40 | + private String badgeText; | |
| 41 | + | |
| 42 | + @Schema(description = "角标颜色", example = "#FF0000") | |
| 43 | + private String badgeColor; | |
| 44 | + | |
| 45 | + @Schema(description = "统计数量(动态查询)", example = "18") | |
| 46 | + private Integer statisticCount; | |
| 47 | + | |
| 48 | + @Schema(description = "是否配置统计标识", example = "true") | |
| 49 | + private Boolean hasStatistic; | |
| 50 | + | |
| 51 | + @Schema(description = "扩展字段 JSON", example = "{}") | |
| 52 | + private Map<String, Object> extra; | |
| 53 | + | |
| 54 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/convert/appmodule/AppModuleConvert.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.convert.appmodule; | |
| 2 | + | |
| 3 | +import com.zteits.urbanops.module.system.controller.admin.appmodule.vo.AppModuleRespVO; | |
| 4 | +import com.zteits.urbanops.module.system.controller.admin.appmodule.vo.AppModuleSaveVO; | |
| 5 | +import com.zteits.urbanops.module.system.controller.admin.appmodule.vo.AppModuleSimpleRespVO; | |
| 6 | +import com.zteits.urbanops.module.system.controller.app.appmodule.vo.AppModuleVO; | |
| 7 | +import com.zteits.urbanops.module.system.dal.dataobject.appmodule.AppModuleDO; | |
| 8 | +import org.mapstruct.AfterMapping; | |
| 9 | +import org.mapstruct.Mapper; | |
| 10 | +import org.mapstruct.MappingTarget; | |
| 11 | +import org.mapstruct.factory.Mappers; | |
| 12 | + | |
| 13 | +import java.util.List; | |
| 14 | + | |
| 15 | +/** | |
| 16 | + * App 功能模块 Convert | |
| 17 | + * | |
| 18 | + * @author system | |
| 19 | + */ | |
| 20 | +@Mapper | |
| 21 | +public interface AppModuleConvert { | |
| 22 | + | |
| 23 | + AppModuleConvert INSTANCE = Mappers.getMapper(AppModuleConvert.class); | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * DO 转 RespVO | |
| 27 | + */ | |
| 28 | + AppModuleRespVO convert(AppModuleDO moduleDO); | |
| 29 | + | |
| 30 | + /** | |
| 31 | + * DO 列表转 RespVO 列表 | |
| 32 | + */ | |
| 33 | + List<AppModuleRespVO> convertList(List<AppModuleDO> list); | |
| 34 | + | |
| 35 | + /** | |
| 36 | + * DO 列表转 SimpleRespVO 列表 | |
| 37 | + */ | |
| 38 | + List<AppModuleSimpleRespVO> convertSimpleList(List<AppModuleDO> list); | |
| 39 | + | |
| 40 | + /** | |
| 41 | + * SaveVO 转 DO | |
| 42 | + */ | |
| 43 | + AppModuleDO convert(AppModuleSaveVO saveVO); | |
| 44 | + | |
| 45 | + /** | |
| 46 | + * DO 列表转 App VO 列表 | |
| 47 | + */ | |
| 48 | + List<AppModuleVO> convertAppList(List<AppModuleDO> list); | |
| 49 | + | |
| 50 | + /** | |
| 51 | + * DO 转 App VO(供列表转换时复用) | |
| 52 | + */ | |
| 53 | + AppModuleVO convertApp(AppModuleDO moduleDO); | |
| 54 | + | |
| 55 | + /** | |
| 56 | + * 映射后填充 statisticCount 与 hasStatistic | |
| 57 | + */ | |
| 58 | + @AfterMapping | |
| 59 | + default void fillStatisticCount(AppModuleDO source, @MappingTarget AppModuleVO target) { | |
| 60 | + if (source == null) { | |
| 61 | + return; | |
| 62 | + } | |
| 63 | + | |
| 64 | + // 统计数量(来源于 DO.extra.statisticCount) | |
| 65 | + if (source.getExtra() != null) { | |
| 66 | + Object statisticCount = source.getExtra().get("statisticCount"); | |
| 67 | + if (statisticCount instanceof Integer) { | |
| 68 | + target.setStatisticCount((Integer) statisticCount); | |
| 69 | + } else if (statisticCount instanceof Number) { | |
| 70 | + target.setStatisticCount(((Number) statisticCount).intValue()); | |
| 71 | + } | |
| 72 | + } | |
| 73 | + | |
| 74 | + // 是否配置统计(来源于 DO.extra.statisticKey 是否存在且非空) | |
| 75 | + boolean hasStatistic = false; | |
| 76 | + if (source.getExtra() != null) { | |
| 77 | + Object statisticKeyObj = source.getExtra().get("statisticKey"); | |
| 78 | + if (statisticKeyObj instanceof String) { | |
| 79 | + String statisticKey = ((String) statisticKeyObj).trim(); | |
| 80 | + hasStatistic = !statisticKey.isEmpty(); | |
| 81 | + } | |
| 82 | + } | |
| 83 | + target.setHasStatistic(hasStatistic); | |
| 84 | + } | |
| 85 | + | |
| 86 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/dal/dataobject/appmodule/AppModuleDO.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.dal.dataobject.appmodule; | |
| 2 | + | |
| 3 | +import com.baomidou.mybatisplus.annotation.FieldStrategy; | |
| 4 | +import com.baomidou.mybatisplus.annotation.KeySequence; | |
| 5 | +import com.baomidou.mybatisplus.annotation.TableField; | |
| 6 | +import com.baomidou.mybatisplus.annotation.TableId; | |
| 7 | +import com.baomidou.mybatisplus.annotation.TableName; | |
| 8 | +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; | |
| 9 | +import com.zteits.urbanops.framework.common.enums.CommonStatusEnum; | |
| 10 | +import com.zteits.urbanops.framework.mybatis.core.dataobject.BaseDO; | |
| 11 | +import com.zteits.urbanops.module.system.enums.appmodule.AppModuleTypeEnum; | |
| 12 | +import com.zteits.urbanops.module.system.enums.appmodule.AppModuleVisibleScopeEnum; | |
| 13 | +import lombok.Data; | |
| 14 | +import lombok.EqualsAndHashCode; | |
| 15 | + | |
| 16 | +import java.util.List; | |
| 17 | +import java.util.Map; | |
| 18 | + | |
| 19 | +/** | |
| 20 | + * App 功能模块 DO | |
| 21 | + * | |
| 22 | + * @author system | |
| 23 | + */ | |
| 24 | +@TableName(value = "system_app_module", autoResultMap = true) | |
| 25 | +@KeySequence("system_app_module_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 | |
| 26 | +@Data | |
| 27 | +@EqualsAndHashCode(callSuper = true) | |
| 28 | +public class AppModuleDO extends BaseDO { | |
| 29 | + | |
| 30 | + /** | |
| 31 | + * 模块编号 - 根节点 | |
| 32 | + */ | |
| 33 | + public static final Long ID_ROOT = 0L; | |
| 34 | + | |
| 35 | + /** | |
| 36 | + * 模块编号 | |
| 37 | + */ | |
| 38 | + @TableId | |
| 39 | + private Long id; | |
| 40 | + | |
| 41 | + /** | |
| 42 | + * 模块名称(主标题) | |
| 43 | + */ | |
| 44 | + private String name; | |
| 45 | + | |
| 46 | + /** | |
| 47 | + * 模块副标题 | |
| 48 | + */ | |
| 49 | + private String subtitle; | |
| 50 | + | |
| 51 | + /** | |
| 52 | + * 模块类型 | |
| 53 | + * | |
| 54 | + * 枚举 {@link AppModuleTypeEnum} | |
| 55 | + */ | |
| 56 | + private Integer type; | |
| 57 | + | |
| 58 | + /** | |
| 59 | + * 显示顺序 | |
| 60 | + */ | |
| 61 | + private Integer sort; | |
| 62 | + | |
| 63 | + /** | |
| 64 | + * 父模块ID | |
| 65 | + */ | |
| 66 | + private Long parentId; | |
| 67 | + | |
| 68 | + /** | |
| 69 | + * 模块图标 | |
| 70 | + */ | |
| 71 | + private String icon; | |
| 72 | + | |
| 73 | + /** | |
| 74 | + * 跳转路径 | |
| 75 | + * | |
| 76 | + * - 原生页面: 原生路由标识 (如: /user/profile) | |
| 77 | + * - H5页面: H5 URL (如: https://www.example.com/page) | |
| 78 | + * - 小程序: 小程序路径 (如: /pages/index/index) | |
| 79 | + * - 外部链接: 完整 URL (如: https://www.example.com) | |
| 80 | + */ | |
| 81 | + private String jumpUrl; | |
| 82 | + | |
| 83 | + /** | |
| 84 | + * 小程序 AppId | |
| 85 | + * | |
| 86 | + * 当 type 为小程序时使用 | |
| 87 | + */ | |
| 88 | + private String miniAppId; | |
| 89 | + | |
| 90 | + /** | |
| 91 | + * 状态 | |
| 92 | + * | |
| 93 | + * 枚举 {@link CommonStatusEnum} | |
| 94 | + */ | |
| 95 | + private Integer status; | |
| 96 | + | |
| 97 | + /** | |
| 98 | + * 是否可见 | |
| 99 | + * | |
| 100 | + * true: 展示在首页/列表中 | |
| 101 | + * false: 不展示但可以通过路径访问 | |
| 102 | + */ | |
| 103 | + private Boolean visible; | |
| 104 | + | |
| 105 | + /** | |
| 106 | + * 可见范围 | |
| 107 | + * | |
| 108 | + * 枚举 {@link AppModuleVisibleScopeEnum} | |
| 109 | + */ | |
| 110 | + private Integer visibleScope; | |
| 111 | + | |
| 112 | + /** | |
| 113 | + * 可见范围配置 | |
| 114 | + * | |
| 115 | + * JSON 格式存储具体配置 | |
| 116 | + * - USER_TYPE: ["VIP", "NORMAL"] | |
| 117 | + * - USER_TAG: ["tag1", "tag2"] | |
| 118 | + * - USER_ID: [123, 456] | |
| 119 | + * - ROLE_ID: ["ROLE_ADMIN", "ROLE_USER"] | |
| 120 | + */ | |
| 121 | + @TableField(typeHandler = JacksonTypeHandler.class, updateStrategy = FieldStrategy.ALWAYS) | |
| 122 | + private List<?> visibleConfig; | |
| 123 | + | |
| 124 | + /** | |
| 125 | + * 是否支持灰度发布 | |
| 126 | + */ | |
| 127 | + private Boolean grayRelease; | |
| 128 | + | |
| 129 | + /** | |
| 130 | + * 灰度发布配置 | |
| 131 | + * | |
| 132 | + * JSON 格式存储灰度规则 | |
| 133 | + */ | |
| 134 | + @TableField(typeHandler = JacksonTypeHandler.class) | |
| 135 | + private Map<String, Object> grayConfig; | |
| 136 | + | |
| 137 | + /** | |
| 138 | + * 角标文本 | |
| 139 | + * | |
| 140 | + * 如: "NEW", "HOT", "1" | |
| 141 | + */ | |
| 142 | + private String badgeText; | |
| 143 | + | |
| 144 | + /** | |
| 145 | + * 角标颜色 | |
| 146 | + * | |
| 147 | + * 如: "#FF0000" | |
| 148 | + */ | |
| 149 | + private String badgeColor; | |
| 150 | + | |
| 151 | + /** | |
| 152 | + * 扩展字段 | |
| 153 | + * | |
| 154 | + * JSON 格式存储其他业务需要的字段 | |
| 155 | + */ | |
| 156 | + @TableField(typeHandler = JacksonTypeHandler.class) | |
| 157 | + private Map<String, Object> extra; | |
| 158 | + | |
| 159 | + /** | |
| 160 | + * 备注 | |
| 161 | + */ | |
| 162 | + private String remark; | |
| 163 | + | |
| 164 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/dal/mysql/appmodule/AppModuleMapper.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.dal.mysql.appmodule; | |
| 2 | + | |
| 3 | +import com.zteits.urbanops.framework.mybatis.core.mapper.BaseMapperX; | |
| 4 | +import com.zteits.urbanops.framework.mybatis.core.query.LambdaQueryWrapperX; | |
| 5 | +import com.zteits.urbanops.module.system.controller.admin.appmodule.vo.AppModuleListReqVO; | |
| 6 | +import com.zteits.urbanops.module.system.dal.dataobject.appmodule.AppModuleDO; | |
| 7 | +import org.apache.ibatis.annotations.Mapper; | |
| 8 | + | |
| 9 | +import java.util.List; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * App 功能模块 Mapper | |
| 13 | + * | |
| 14 | + * @author system | |
| 15 | + */ | |
| 16 | +@Mapper | |
| 17 | +public interface AppModuleMapper extends BaseMapperX<AppModuleDO> { | |
| 18 | + | |
| 19 | + /** | |
| 20 | + * 根据父模块ID和名称查询 | |
| 21 | + * | |
| 22 | + * @param parentId 父模块ID | |
| 23 | + * @param name 模块名称 | |
| 24 | + * @return App 功能模块 | |
| 25 | + */ | |
| 26 | + default AppModuleDO selectByParentIdAndName(Long parentId, String name) { | |
| 27 | + return selectOne(AppModuleDO::getParentId, parentId, AppModuleDO::getName, name); | |
| 28 | + } | |
| 29 | + | |
| 30 | + /** | |
| 31 | + * 统计父模块下的子模块数量 | |
| 32 | + * | |
| 33 | + * @param parentId 父模块ID | |
| 34 | + * @return 子模块数量 | |
| 35 | + */ | |
| 36 | + default Long selectCountByParentId(Long parentId) { | |
| 37 | + return selectCount(AppModuleDO::getParentId, parentId); | |
| 38 | + } | |
| 39 | + | |
| 40 | + /** | |
| 41 | + * 查询模块列表 | |
| 42 | + * | |
| 43 | + * @param reqVO 查询条件 | |
| 44 | + * @return 模块列表 | |
| 45 | + */ | |
| 46 | + default List<AppModuleDO> selectList(AppModuleListReqVO reqVO) { | |
| 47 | + return selectList(new LambdaQueryWrapperX<AppModuleDO>() | |
| 48 | + .likeIfPresent(AppModuleDO::getName, reqVO.getName()) | |
| 49 | + .eqIfPresent(AppModuleDO::getStatus, reqVO.getStatus()) | |
| 50 | + .eqIfPresent(AppModuleDO::getType, reqVO.getType()) | |
| 51 | + .orderByAsc(AppModuleDO::getSort)); | |
| 52 | + } | |
| 53 | + | |
| 54 | + /** | |
| 55 | + * 根据父模块ID查询子模块列表 | |
| 56 | + * | |
| 57 | + * @param parentId 父模块ID | |
| 58 | + * @return 子模块列表 | |
| 59 | + */ | |
| 60 | + default List<AppModuleDO> selectListByParentId(Long parentId) { | |
| 61 | + return selectList(AppModuleDO::getParentId, parentId); | |
| 62 | + } | |
| 63 | + | |
| 64 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/enums/ErrorCodeConstants.java
| ... | ... | @@ -168,4 +168,11 @@ public interface ErrorCodeConstants { |
| 168 | 168 | // ========== 站内信发送 1-002-028-000 ========== |
| 169 | 169 | ErrorCode NOTIFY_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_028_000, "模板参数({})缺失"); |
| 170 | 170 | |
| 171 | + // ========== App 功能模块 1-002-029-000 ========== | |
| 172 | + ErrorCode APP_MODULE_NOT_EXISTS = new ErrorCode(1_002_029_000, "App 功能模块不存在"); | |
| 173 | + ErrorCode APP_MODULE_NAME_DUPLICATE = new ErrorCode(1_002_029_001, "已经存在该名字的 App 功能模块"); | |
| 174 | + ErrorCode APP_MODULE_PARENT_NOT_EXISTS = new ErrorCode(1_002_029_002, "父模块不存在"); | |
| 175 | + ErrorCode APP_MODULE_PARENT_ERROR = new ErrorCode(1_002_029_003, "不能设置自己为父模块"); | |
| 176 | + ErrorCode APP_MODULE_EXISTS_CHILDREN = new ErrorCode(1_002_029_004, "存在子模块,无法删除"); | |
| 177 | + ErrorCode APP_MODULE_PARENT_NOT_CATALOG = new ErrorCode(1_002_029_005, "父模块必须是目录类型"); | |
| 171 | 178 | } | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/enums/appmodule/AppModuleTypeEnum.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.enums.appmodule; | |
| 2 | + | |
| 3 | +import lombok.AllArgsConstructor; | |
| 4 | +import lombok.Getter; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * App 功能模块类型枚举 | |
| 8 | + * | |
| 9 | + * @author system | |
| 10 | + */ | |
| 11 | +@Getter | |
| 12 | +@AllArgsConstructor | |
| 13 | +public enum AppModuleTypeEnum { | |
| 14 | + | |
| 15 | + /** | |
| 16 | + * 目录 | |
| 17 | + */ | |
| 18 | + CATALOG(1, "目录"), | |
| 19 | + | |
| 20 | + /** | |
| 21 | + * 原生页面 | |
| 22 | + */ | |
| 23 | + NATIVE(2, "原生页面"), | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * H5页面 | |
| 27 | + */ | |
| 28 | + H5(3, "H5页面"), | |
| 29 | + | |
| 30 | + /** | |
| 31 | + * 小程序 | |
| 32 | + */ | |
| 33 | + MINI_PROGRAM(4, "小程序"), | |
| 34 | + | |
| 35 | + /** | |
| 36 | + * 外部链接 | |
| 37 | + */ | |
| 38 | + EXTERNAL_LINK(5, "外部链接"); | |
| 39 | + | |
| 40 | + /** | |
| 41 | + * 类型 | |
| 42 | + */ | |
| 43 | + private final Integer type; | |
| 44 | + | |
| 45 | + /** | |
| 46 | + * 类型名 | |
| 47 | + */ | |
| 48 | + private final String name; | |
| 49 | + | |
| 50 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/enums/appmodule/AppModuleVisibleScopeEnum.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.enums.appmodule; | |
| 2 | + | |
| 3 | +import lombok.AllArgsConstructor; | |
| 4 | +import lombok.Getter; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * App 功能模块可见范围枚举 | |
| 8 | + * | |
| 9 | + * @author system | |
| 10 | + */ | |
| 11 | +@Getter | |
| 12 | +@AllArgsConstructor | |
| 13 | +public enum AppModuleVisibleScopeEnum { | |
| 14 | + | |
| 15 | + /** | |
| 16 | + * 所有用户 | |
| 17 | + */ | |
| 18 | + ALL(1, "所有用户"), | |
| 19 | + | |
| 20 | + /** | |
| 21 | + * 指定用户类型 | |
| 22 | + */ | |
| 23 | + USER_TYPE(2, "指定用户类型"), | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * 指定用户标签 | |
| 27 | + */ | |
| 28 | + USER_TAG(3, "指定用户标签"), | |
| 29 | + | |
| 30 | + /** | |
| 31 | + * 指定用户ID | |
| 32 | + */ | |
| 33 | + USER_ID(4, "指定用户ID"), | |
| 34 | + /** | |
| 35 | + * 指定角色编码 | |
| 36 | + */ | |
| 37 | + ROLE_ID(5, "指定角色编码"); | |
| 38 | + | |
| 39 | + /** | |
| 40 | + * 范围 | |
| 41 | + */ | |
| 42 | + private final Integer scope; | |
| 43 | + | |
| 44 | + /** | |
| 45 | + * 范围名 | |
| 46 | + */ | |
| 47 | + private final String name; | |
| 48 | + | |
| 49 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/appmodule/AppModuleService.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.service.appmodule; | |
| 2 | + | |
| 3 | + | |
| 4 | +import com.zteits.urbanops.module.system.controller.admin.appmodule.vo.AppModuleListReqVO; | |
| 5 | +import com.zteits.urbanops.module.system.controller.admin.appmodule.vo.AppModuleSaveVO; | |
| 6 | +import com.zteits.urbanops.module.system.dal.dataobject.appmodule.AppModuleDO; | |
| 7 | + | |
| 8 | +import java.util.Collection; | |
| 9 | +import java.util.List; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * App 功能模块 Service 接口 | |
| 13 | + * | |
| 14 | + * @author system | |
| 15 | + */ | |
| 16 | +public interface AppModuleService { | |
| 17 | + | |
| 18 | + /** | |
| 19 | + * 创建 App 功能模块 | |
| 20 | + * | |
| 21 | + * @param createReqVO 创建信息 | |
| 22 | + * @return 模块编号 | |
| 23 | + */ | |
| 24 | + Long createAppModule(AppModuleSaveVO createReqVO); | |
| 25 | + | |
| 26 | + /** | |
| 27 | + * 更新 App 功能模块 | |
| 28 | + * | |
| 29 | + * @param updateReqVO 更新信息 | |
| 30 | + */ | |
| 31 | + void updateAppModule(AppModuleSaveVO updateReqVO); | |
| 32 | + | |
| 33 | + /** | |
| 34 | + * 删除 App 功能模块 | |
| 35 | + * | |
| 36 | + * @param id 模块编号 | |
| 37 | + */ | |
| 38 | + void deleteAppModule(Long id); | |
| 39 | + | |
| 40 | + /** | |
| 41 | + * 批量删除 App 功能模块 | |
| 42 | + * | |
| 43 | + * @param ids 模块编号数组 | |
| 44 | + */ | |
| 45 | + void deleteAppModuleList(List<Long> ids); | |
| 46 | + | |
| 47 | + /** | |
| 48 | + * 获得 App 功能模块 | |
| 49 | + * | |
| 50 | + * @param id 模块编号 | |
| 51 | + * @return App 功能模块 | |
| 52 | + */ | |
| 53 | + AppModuleDO getAppModule(Long id); | |
| 54 | + | |
| 55 | + /** | |
| 56 | + * 获得 App 功能模块数组 | |
| 57 | + * | |
| 58 | + * @param ids 模块编号数组 | |
| 59 | + * @return App 功能模块数组 | |
| 60 | + */ | |
| 61 | + List<AppModuleDO> getAppModuleList(Collection<Long> ids); | |
| 62 | + | |
| 63 | + /** | |
| 64 | + * 获得所有 App 功能模块列表 | |
| 65 | + * | |
| 66 | + * @return 模块列表 | |
| 67 | + */ | |
| 68 | + List<AppModuleDO> getAppModuleList(); | |
| 69 | + | |
| 70 | + /** | |
| 71 | + * 筛选 App 功能模块列表 | |
| 72 | + * | |
| 73 | + * @param reqVO 筛选条件 | |
| 74 | + * @return 模块列表 | |
| 75 | + */ | |
| 76 | + List<AppModuleDO> getAppModuleList(AppModuleListReqVO reqVO); | |
| 77 | + | |
| 78 | + /** | |
| 79 | + * 获取用户可见的 App 功能模块列表 | |
| 80 | + * | |
| 81 | + * @param userId 用户ID | |
| 82 | + * @param reqVO 筛选条件 | |
| 83 | + * @return 模块列表 | |
| 84 | + */ | |
| 85 | + List<AppModuleDO> getVisibleAppModuleList(Long userId, AppModuleListReqVO reqVO); | |
| 86 | + | |
| 87 | + /** | |
| 88 | + * 过滤掉关闭的模块及其子模块 | |
| 89 | + * | |
| 90 | + * @param list 模块列表 | |
| 91 | + * @return 过滤后的模块列表 | |
| 92 | + */ | |
| 93 | + List<AppModuleDO> filterDisableModules(List<AppModuleDO> list); | |
| 94 | + | |
| 95 | + /** | |
| 96 | + * 填充模块统计数据 | |
| 97 | + * | |
| 98 | + * @param modules 模块列表 | |
| 99 | + * @param userId 用户ID | |
| 100 | + */ | |
| 101 | + void fillModuleStatistics(List<AppModuleDO> modules, Long userId); | |
| 102 | + | |
| 103 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/appmodule/AppModuleServiceImpl.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.service.appmodule; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.util.ObjUtil; | |
| 5 | +import cn.hutool.core.util.StrUtil; | |
| 6 | +import com.google.common.collect.Lists; | |
| 7 | +import com.zteits.urbanops.framework.common.enums.CommonStatusEnum; | |
| 8 | +import com.zteits.urbanops.framework.common.util.object.BeanUtils; | |
| 9 | +import com.zteits.urbanops.module.system.controller.admin.appmodule.vo.AppModuleListReqVO; | |
| 10 | +import com.zteits.urbanops.module.system.controller.admin.appmodule.vo.AppModuleSaveVO; | |
| 11 | +import com.zteits.urbanops.module.system.dal.dataobject.appmodule.AppModuleDO; | |
| 12 | +import com.zteits.urbanops.module.system.dal.mysql.appmodule.AppModuleMapper; | |
| 13 | +import com.zteits.urbanops.module.system.enums.appmodule.AppModuleTypeEnum; | |
| 14 | +import com.zteits.urbanops.module.system.enums.appmodule.AppModuleVisibleScopeEnum; | |
| 15 | +import com.zteits.urbanops.module.system.service.appmodule.statistics.StatisticsProviderRegistry; | |
| 16 | +import com.zteits.urbanops.module.system.service.appmodule.strategy.GrayReleaseStrategy; | |
| 17 | +import com.zteits.urbanops.module.system.service.appmodule.strategy.GrayReleaseStrategyFactory; | |
| 18 | +import com.zteits.urbanops.module.system.service.appmodule.strategy.VisibleScopeStrategy; | |
| 19 | +import com.zteits.urbanops.module.system.service.appmodule.strategy.VisibleScopeStrategyFactory; | |
| 20 | +import lombok.AllArgsConstructor; | |
| 21 | +import lombok.extern.slf4j.Slf4j; | |
| 22 | +import org.springframework.stereotype.Service; | |
| 23 | +import org.springframework.transaction.annotation.Transactional; | |
| 24 | +import org.springframework.validation.annotation.Validated; | |
| 25 | + | |
| 26 | +import java.util.*; | |
| 27 | + | |
| 28 | +import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception; | |
| 29 | +import static com.zteits.urbanops.module.system.dal.dataobject.appmodule.AppModuleDO.ID_ROOT; | |
| 30 | +import static com.zteits.urbanops.module.system.enums.ErrorCodeConstants.*; | |
| 31 | + | |
| 32 | + | |
| 33 | +/** | |
| 34 | + * App 功能模块 Service 实现类 | |
| 35 | + * | |
| 36 | + * @author system | |
| 37 | + */ | |
| 38 | +@Service | |
| 39 | +@Validated | |
| 40 | +@Slf4j | |
| 41 | +@AllArgsConstructor | |
| 42 | +public class AppModuleServiceImpl implements AppModuleService { | |
| 43 | + | |
| 44 | + private final AppModuleMapper appModuleMapper; | |
| 45 | + | |
| 46 | + private final VisibleScopeStrategyFactory visibleScopeStrategyFactory; | |
| 47 | + | |
| 48 | + private final GrayReleaseStrategyFactory grayReleaseStrategyFactory; | |
| 49 | + | |
| 50 | + private final StatisticsProviderRegistry statisticsProviderRegistry; | |
| 51 | + | |
| 52 | + | |
| 53 | + @Override | |
| 54 | + @Transactional(rollbackFor = Exception.class) | |
| 55 | + public Long createAppModule(AppModuleSaveVO createReqVO) { | |
| 56 | + // 校验父模块存在 | |
| 57 | + validateParentModule(createReqVO.getParentId(), null); | |
| 58 | + // 校验模块名称唯一性 | |
| 59 | + validateModuleName(createReqVO.getParentId(), createReqVO.getName(), null); | |
| 60 | + | |
| 61 | + // 插入到数据库 | |
| 62 | + AppModuleDO module = BeanUtils.toBean(createReqVO, AppModuleDO.class); | |
| 63 | + initModuleProperty(module); | |
| 64 | + appModuleMapper.insert(module); | |
| 65 | + return module.getId(); | |
| 66 | + } | |
| 67 | + | |
| 68 | + @Override | |
| 69 | + @Transactional(rollbackFor = Exception.class) | |
| 70 | + public void updateAppModule(AppModuleSaveVO updateReqVO) { | |
| 71 | + // 校验更新的模块是否存在 | |
| 72 | + if (appModuleMapper.selectById(updateReqVO.getId()) == null) { | |
| 73 | + throw exception(APP_MODULE_NOT_EXISTS); | |
| 74 | + } | |
| 75 | + // 校验父模块存在 | |
| 76 | + validateParentModule(updateReqVO.getParentId(), updateReqVO.getId()); | |
| 77 | + // 校验模块名称唯一性 | |
| 78 | + validateModuleName(updateReqVO.getParentId(), updateReqVO.getName(), updateReqVO.getId()); | |
| 79 | + | |
| 80 | + // 更新到数据库 | |
| 81 | + AppModuleDO updateObj = BeanUtils.toBean(updateReqVO, AppModuleDO.class); | |
| 82 | + initModuleProperty(updateObj); | |
| 83 | + appModuleMapper.updateById(updateObj); | |
| 84 | + } | |
| 85 | + | |
| 86 | + @Override | |
| 87 | + @Transactional(rollbackFor = Exception.class) | |
| 88 | + public void deleteAppModule(Long id) { | |
| 89 | + // 校验是否还有子模块 | |
| 90 | + if (appModuleMapper.selectCountByParentId(id) > 0) { | |
| 91 | + throw exception(APP_MODULE_EXISTS_CHILDREN); | |
| 92 | + } | |
| 93 | + // 校验删除的模块是否存在 | |
| 94 | + if (appModuleMapper.selectById(id) == null) { | |
| 95 | + throw exception(APP_MODULE_NOT_EXISTS); | |
| 96 | + } | |
| 97 | + // 标记删除 | |
| 98 | + appModuleMapper.deleteById(id); | |
| 99 | + } | |
| 100 | + | |
| 101 | + @Override | |
| 102 | + @Transactional(rollbackFor = Exception.class) | |
| 103 | + public void deleteAppModuleList(List<Long> ids) { | |
| 104 | + // 校验是否还有子模块 | |
| 105 | + ids.forEach(id -> { | |
| 106 | + if (appModuleMapper.selectCountByParentId(id) > 0) { | |
| 107 | + throw exception(APP_MODULE_EXISTS_CHILDREN); | |
| 108 | + } | |
| 109 | + }); | |
| 110 | + | |
| 111 | + // 标记删除 | |
| 112 | + appModuleMapper.deleteByIds(ids); | |
| 113 | + } | |
| 114 | + | |
| 115 | + @Override | |
| 116 | + public AppModuleDO getAppModule(Long id) { | |
| 117 | + return appModuleMapper.selectById(id); | |
| 118 | + } | |
| 119 | + | |
| 120 | + @Override | |
| 121 | + public List<AppModuleDO> getAppModuleList(Collection<Long> ids) { | |
| 122 | + // 当 ids 为空时,返回一个空的实例对象 | |
| 123 | + if (CollUtil.isEmpty(ids)) { | |
| 124 | + return Lists.newArrayList(); | |
| 125 | + } | |
| 126 | + return appModuleMapper.selectByIds(ids); | |
| 127 | + } | |
| 128 | + | |
| 129 | + @Override | |
| 130 | + public List<AppModuleDO> getAppModuleList() { | |
| 131 | + return appModuleMapper.selectList(); | |
| 132 | + } | |
| 133 | + | |
| 134 | + @Override | |
| 135 | + public List<AppModuleDO> getAppModuleList(AppModuleListReqVO reqVO) { | |
| 136 | + return appModuleMapper.selectList(reqVO); | |
| 137 | + } | |
| 138 | + | |
| 139 | + @Override | |
| 140 | + public List<AppModuleDO> getVisibleAppModuleList(Long userId, AppModuleListReqVO reqVO) { | |
| 141 | + | |
| 142 | + | |
| 143 | + // 查询所有启用的模块 | |
| 144 | + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 145 | + List<AppModuleDO> modules = getAppModuleList(reqVO); | |
| 146 | + | |
| 147 | + // 过滤掉关闭的节点 | |
| 148 | + modules = filterDisableModules(modules); | |
| 149 | + | |
| 150 | + // 根据用户权限、可见范围等进行过滤 | |
| 151 | + modules = filterByVisibleScope(modules, userId); | |
| 152 | + | |
| 153 | + // 灰度发布过滤 | |
| 154 | + modules = filterByGrayRelease(modules, userId); | |
| 155 | + | |
| 156 | + // 父子级联权限过滤 | |
| 157 | + modules = filterByParentPermission(modules); | |
| 158 | + | |
| 159 | + return modules; | |
| 160 | + } | |
| 161 | + | |
| 162 | + @Override | |
| 163 | + public List<AppModuleDO> filterDisableModules(List<AppModuleDO> moduleList) { | |
| 164 | + if (CollUtil.isEmpty(moduleList)) { | |
| 165 | + return Collections.emptyList(); | |
| 166 | + } | |
| 167 | + Map<Long, AppModuleDO> moduleMap = new HashMap<>(); | |
| 168 | + for (AppModuleDO module : moduleList) { | |
| 169 | + moduleMap.put(module.getId(), module); | |
| 170 | + } | |
| 171 | + | |
| 172 | + // 遍历模块,查找不是禁用的模块,添加到结果 | |
| 173 | + List<AppModuleDO> enabledModules = new ArrayList<>(); | |
| 174 | + Set<Long> disabledModuleCache = new HashSet<>(); | |
| 175 | + for (AppModuleDO module : moduleList) { | |
| 176 | + if (!isModuleDisabled(module, moduleMap, disabledModuleCache)) { | |
| 177 | + enabledModules.add(module); | |
| 178 | + } | |
| 179 | + } | |
| 180 | + return enabledModules; | |
| 181 | + } | |
| 182 | + | |
| 183 | + /** | |
| 184 | + * 判断模块是否被禁用(包括父模块) | |
| 185 | + */ | |
| 186 | + private boolean isModuleDisabled(AppModuleDO node, Map<Long, AppModuleDO> moduleMap, Set<Long> disabledModuleCache) { | |
| 187 | + // 如果已经判定是禁用的节点,直接结束 | |
| 188 | + if (disabledModuleCache.contains(node.getId())) { | |
| 189 | + return true; | |
| 190 | + } | |
| 191 | + | |
| 192 | + // 1. 先判断自身是否禁用 | |
| 193 | + if (CommonStatusEnum.isDisable(node.getStatus())) { | |
| 194 | + disabledModuleCache.add(node.getId()); | |
| 195 | + return true; | |
| 196 | + } | |
| 197 | + | |
| 198 | + // 2. 判断是否不可见 | |
| 199 | + if (node.getVisible() != null && !node.getVisible()) { | |
| 200 | + // 注意:不可见不一定是禁用,根据业务需求决定 | |
| 201 | + // 这里我们认为不可见的模块在列表中不展示 | |
| 202 | + disabledModuleCache.add(node.getId()); | |
| 203 | + return true; | |
| 204 | + } | |
| 205 | + | |
| 206 | + // 3. 遍历到 parentId 为根节点,则无需判断 | |
| 207 | + Long parentId = node.getParentId(); | |
| 208 | + if (ObjUtil.equal(parentId, ID_ROOT)) { | |
| 209 | + return false; | |
| 210 | + } | |
| 211 | + | |
| 212 | + // 4. 继续遍历 parent 节点 | |
| 213 | + AppModuleDO parent = moduleMap.get(parentId); | |
| 214 | + if (parent == null || isModuleDisabled(parent, moduleMap, disabledModuleCache)) { | |
| 215 | + disabledModuleCache.add(node.getId()); | |
| 216 | + return true; | |
| 217 | + } | |
| 218 | + return false; | |
| 219 | + } | |
| 220 | + | |
| 221 | + /** | |
| 222 | + * 根据可见范围过滤模块 | |
| 223 | + * | |
| 224 | + * @param modules 模块列表 | |
| 225 | + * @param userId 用户ID | |
| 226 | + * @return 过滤后的模块列表 | |
| 227 | + */ | |
| 228 | + private List<AppModuleDO> filterByVisibleScope(List<AppModuleDO> modules, Long userId) { | |
| 229 | + if (CollUtil.isEmpty(modules)) { | |
| 230 | + return modules; | |
| 231 | + } | |
| 232 | + | |
| 233 | + // 如果用户未登录,只返回所有用户可见的模块 | |
| 234 | + if (userId == null) { | |
| 235 | + List<AppModuleDO> result = new ArrayList<>(); | |
| 236 | + for (AppModuleDO module : modules) { | |
| 237 | + if (AppModuleVisibleScopeEnum.ALL.getScope().equals(module.getVisibleScope())) { | |
| 238 | + result.add(module); | |
| 239 | + } | |
| 240 | + } | |
| 241 | + return result; | |
| 242 | + } | |
| 243 | + | |
| 244 | + List<AppModuleDO> result = new ArrayList<>(); | |
| 245 | + for (AppModuleDO module : modules) { | |
| 246 | + if (isModuleVisibleToUser(module, userId)) { | |
| 247 | + result.add(module); | |
| 248 | + } | |
| 249 | + } | |
| 250 | + return result; | |
| 251 | + } | |
| 252 | + | |
| 253 | + /** | |
| 254 | + * 判断模块对用户是否可见 | |
| 255 | + * | |
| 256 | + * @param module 模块 | |
| 257 | + * @param userId 用户ID | |
| 258 | + * @return 是否可见 | |
| 259 | + */ | |
| 260 | + private boolean isModuleVisibleToUser(AppModuleDO module, Long userId) { | |
| 261 | + Integer visibleScope = module.getVisibleScope(); | |
| 262 | + if (visibleScope == null) { | |
| 263 | + visibleScope = AppModuleVisibleScopeEnum.ALL.getScope(); | |
| 264 | + } | |
| 265 | + | |
| 266 | + VisibleScopeStrategy strategy = visibleScopeStrategyFactory.getStrategy(visibleScope); | |
| 267 | + if (strategy == null) { | |
| 268 | + log.warn("[可见范围策略] 未找到对应策略. visibleScope={}", visibleScope); | |
| 269 | + return false; | |
| 270 | + } | |
| 271 | + | |
| 272 | + return strategy.isVisible(module, userId); | |
| 273 | + } | |
| 274 | + | |
| 275 | + /** | |
| 276 | + * 根据灰度发布过滤模块 | |
| 277 | + * | |
| 278 | + * @param modules 模块列表 | |
| 279 | + * @param userId 用户ID | |
| 280 | + * @return 过滤后的模块列表 | |
| 281 | + */ | |
| 282 | + private List<AppModuleDO> filterByGrayRelease(List<AppModuleDO> modules, Long userId) { | |
| 283 | + if (CollUtil.isEmpty(modules) || userId == null) { | |
| 284 | + return modules; | |
| 285 | + } | |
| 286 | + | |
| 287 | + List<AppModuleDO> result = new ArrayList<>(); | |
| 288 | + for (AppModuleDO module : modules) { | |
| 289 | + // 如果模块没有启用灰度发布,直接通过 | |
| 290 | + if (module.getGrayRelease() == null || !module.getGrayRelease()) { | |
| 291 | + result.add(module); | |
| 292 | + continue; | |
| 293 | + } | |
| 294 | + // 判断用户是否命中灰度规则 | |
| 295 | + if (isUserInGrayRelease(module.getGrayConfig(), userId)) { | |
| 296 | + result.add(module); | |
| 297 | + } | |
| 298 | + } | |
| 299 | + return result; | |
| 300 | + } | |
| 301 | + | |
| 302 | + /** | |
| 303 | + * 父级不可见时,级联剔除子模块 | |
| 304 | + */ | |
| 305 | + private List<AppModuleDO> filterByParentPermission(List<AppModuleDO> modules) { | |
| 306 | + if (CollUtil.isEmpty(modules)) { | |
| 307 | + return modules; | |
| 308 | + } | |
| 309 | + | |
| 310 | + Map<Long, AppModuleDO> moduleMap = new HashMap<>(); | |
| 311 | + for (AppModuleDO module : modules) { | |
| 312 | + moduleMap.put(module.getId(), module); | |
| 313 | + } | |
| 314 | + | |
| 315 | + Set<Long> allowed = new HashSet<>(); | |
| 316 | + Set<Long> blocked = new HashSet<>(); | |
| 317 | + List<AppModuleDO> result = new ArrayList<>(); | |
| 318 | + for (AppModuleDO module : modules) { | |
| 319 | + if (isParentChainVisible(module, moduleMap, allowed, blocked)) { | |
| 320 | + result.add(module); | |
| 321 | + } | |
| 322 | + } | |
| 323 | + return result; | |
| 324 | + } | |
| 325 | + | |
| 326 | + private boolean isParentChainVisible(AppModuleDO module, Map<Long, AppModuleDO> moduleMap, | |
| 327 | + Set<Long> allowed, Set<Long> blocked) { | |
| 328 | + Long moduleId = module.getId(); | |
| 329 | + if (allowed.contains(moduleId)) { | |
| 330 | + return true; | |
| 331 | + } | |
| 332 | + if (blocked.contains(moduleId)) { | |
| 333 | + return false; | |
| 334 | + } | |
| 335 | + | |
| 336 | + Long parentId = module.getParentId(); | |
| 337 | + if (parentId == null || ObjUtil.equal(parentId, ID_ROOT)) { | |
| 338 | + allowed.add(moduleId); | |
| 339 | + return true; | |
| 340 | + } | |
| 341 | + | |
| 342 | + AppModuleDO parent = moduleMap.get(parentId); | |
| 343 | + if (parent == null) { | |
| 344 | + blocked.add(moduleId); | |
| 345 | + return false; | |
| 346 | + } | |
| 347 | + | |
| 348 | + if (isParentChainVisible(parent, moduleMap, allowed, blocked)) { | |
| 349 | + allowed.add(moduleId); | |
| 350 | + return true; | |
| 351 | + } | |
| 352 | + | |
| 353 | + blocked.add(moduleId); | |
| 354 | + return false; | |
| 355 | + } | |
| 356 | + | |
| 357 | + /** | |
| 358 | + * 判断用户是否命中灰度发布规则 | |
| 359 | + * | |
| 360 | + * @param grayConfig 灰度配置 Map | |
| 361 | + * @param userId 用户ID | |
| 362 | + * @return 是否命中 | |
| 363 | + */ | |
| 364 | + private boolean isUserInGrayRelease(Map<String, Object> grayConfig, Long userId) { | |
| 365 | + if (grayConfig == null || grayConfig.isEmpty()) { | |
| 366 | + return false; | |
| 367 | + } | |
| 368 | + | |
| 369 | + try { | |
| 370 | + String type = (String) grayConfig.get("type"); | |
| 371 | + | |
| 372 | + GrayReleaseStrategy strategy = grayReleaseStrategyFactory.getStrategy(type); | |
| 373 | + if (strategy == null) { | |
| 374 | + log.warn("[灰度发布策略] 未找到对应策略. type={}", type); | |
| 375 | + return false; | |
| 376 | + } | |
| 377 | + | |
| 378 | + return strategy.isMatch(grayConfig, userId); | |
| 379 | + } catch (Exception e) { | |
| 380 | + log.error("[解析灰度发布配置失败] grayConfig={}", grayConfig, e); | |
| 381 | + return false; | |
| 382 | + } | |
| 383 | + } | |
| 384 | + | |
| 385 | + /** | |
| 386 | + * 校验父模块是否合法 | |
| 387 | + * | |
| 388 | + * 1. 不能设置自己为父模块 | |
| 389 | + * 2. 父模块不存在 | |
| 390 | + * 3. 父模块必须是目录类型 | |
| 391 | + */ | |
| 392 | + private void validateParentModule(Long parentId, Long childId) { | |
| 393 | + if (parentId == null || ID_ROOT.equals(parentId)) { | |
| 394 | + return; | |
| 395 | + } | |
| 396 | + // 不能设置自己为父模块 | |
| 397 | + if (parentId.equals(childId)) { | |
| 398 | + throw exception(APP_MODULE_PARENT_ERROR); | |
| 399 | + } | |
| 400 | + AppModuleDO module = appModuleMapper.selectById(parentId); | |
| 401 | + // 父模块不存在 | |
| 402 | + if (module == null) { | |
| 403 | + throw exception(APP_MODULE_PARENT_NOT_EXISTS); | |
| 404 | + } | |
| 405 | + // 父模块必须是目录类型 | |
| 406 | + if (!AppModuleTypeEnum.CATALOG.getType().equals(module.getType())) { | |
| 407 | + throw exception(APP_MODULE_PARENT_NOT_CATALOG); | |
| 408 | + } | |
| 409 | + } | |
| 410 | + | |
| 411 | + /** | |
| 412 | + * 校验模块名称是否合法 | |
| 413 | + * | |
| 414 | + * 1. 校验相同父模块编号下,是否存在相同的模块名 | |
| 415 | + */ | |
| 416 | + private void validateModuleName(Long parentId, String name, Long id) { | |
| 417 | + AppModuleDO module = appModuleMapper.selectByParentIdAndName(parentId, name); | |
| 418 | + if (module == null) { | |
| 419 | + return; | |
| 420 | + } | |
| 421 | + // 如果 id 为空,说明不用比较是否为相同 id 的模块 | |
| 422 | + if (id == null) { | |
| 423 | + throw exception(APP_MODULE_NAME_DUPLICATE); | |
| 424 | + } | |
| 425 | + if (!module.getId().equals(id)) { | |
| 426 | + throw exception(APP_MODULE_NAME_DUPLICATE); | |
| 427 | + } | |
| 428 | + } | |
| 429 | + | |
| 430 | + /** | |
| 431 | + * 初始化模块的通用属性 | |
| 432 | + */ | |
| 433 | + private void initModuleProperty(AppModuleDO module) { | |
| 434 | + // 目录类型时,清空跳转路径等属性 | |
| 435 | + if (AppModuleTypeEnum.CATALOG.getType().equals(module.getType())) { | |
| 436 | + module.setJumpUrl(""); | |
| 437 | + module.setMiniAppId(""); | |
| 438 | + } | |
| 439 | + | |
| 440 | + // 设置默认值 | |
| 441 | + if (module.getVisible() == null) { | |
| 442 | + module.setVisible(true); | |
| 443 | + } | |
| 444 | + if (module.getGrayRelease() == null) { | |
| 445 | + module.setGrayRelease(false); | |
| 446 | + } | |
| 447 | + if (module.getVisibleScope() == null) { | |
| 448 | + module.setVisibleScope(AppModuleVisibleScopeEnum.ALL.getScope()); | |
| 449 | + } | |
| 450 | + } | |
| 451 | + | |
| 452 | + @Override | |
| 453 | + public void fillModuleStatistics(List<AppModuleDO> modules, Long userId) { | |
| 454 | + if (CollUtil.isEmpty(modules) || userId == null) { | |
| 455 | + return; | |
| 456 | + } | |
| 457 | + | |
| 458 | + // 需要统计的模块集合 | |
| 459 | + Map<String, AppModuleDO> statisticModules = new HashMap<>(); | |
| 460 | + | |
| 461 | + for (AppModuleDO module : modules) { | |
| 462 | + // 从 extra 字段中获取统计配置 | |
| 463 | + Map<String, Object> extra = module.getExtra(); | |
| 464 | + if (extra != null && extra.containsKey("statisticKey")) { | |
| 465 | + String statisticKey = (String) extra.get("statisticKey"); | |
| 466 | + if (StrUtil.isNotBlank(statisticKey)) { | |
| 467 | + statisticModules.put(statisticKey, module); | |
| 468 | + } | |
| 469 | + } | |
| 470 | + } | |
| 471 | + | |
| 472 | + if (statisticModules.isEmpty()) { | |
| 473 | + return; | |
| 474 | + } | |
| 475 | + | |
| 476 | + // 并行查询统计数据 | |
| 477 | + Map<String, Integer> statisticCounts = queryStatisticCounts(statisticModules.keySet(), userId); | |
| 478 | + | |
| 479 | + // 填充统计数据 | |
| 480 | + statisticCounts.forEach((key, count) -> { | |
| 481 | + AppModuleDO module = statisticModules.get(key); | |
| 482 | + if (module != null) { | |
| 483 | + // 填充到 extra 字段中 | |
| 484 | + if (module.getExtra() == null) { | |
| 485 | + module.setExtra(new HashMap<>()); | |
| 486 | + } | |
| 487 | + module.getExtra().put("statisticCount", count); | |
| 488 | + } | |
| 489 | + }); | |
| 490 | + } | |
| 491 | + | |
| 492 | + /** | |
| 493 | + * 查询统计数据(根据模块标识) | |
| 494 | + * | |
| 495 | + * @param statisticKeys 统计标识集合 | |
| 496 | + * @param userId 用户ID | |
| 497 | + * @return 统计结果 Map<统计标识, 数量> | |
| 498 | + */ | |
| 499 | + private Map<String, Integer> queryStatisticCounts(Set<String> statisticKeys, Long userId) { | |
| 500 | + Map<String, Integer> result = new HashMap<>(); | |
| 501 | + | |
| 502 | + for (String key : statisticKeys) { | |
| 503 | + try { | |
| 504 | + Integer count = statisticsProviderRegistry.getCount(key, userId); | |
| 505 | + result.put(key, count != null ? count : 0); | |
| 506 | + } catch (Exception e) { | |
| 507 | + log.error("查询模块统计数据失败, statisticKey={}", key, e); | |
| 508 | + result.put(key, 0); | |
| 509 | + } | |
| 510 | + } | |
| 511 | + | |
| 512 | + return result; | |
| 513 | + } | |
| 514 | + | |
| 515 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/appmodule/statistics/StatisticProvider.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.service.appmodule.statistics; | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * 统计数据提供者 SPI | |
| 5 | + * | |
| 6 | + * 实现方(上层业务模块,如 reserve 等)在自身模块中提供具体实现, | |
| 7 | + * system 模块只依赖此接口,不依赖具体实现,避免向上依赖。 | |
| 8 | + */ | |
| 9 | +public interface StatisticProvider { | |
| 10 | + | |
| 11 | + /** | |
| 12 | + * 返回当前提供者负责的统计标识(例如:manager_authenticated、insured_authenticated 等) | |
| 13 | + */ | |
| 14 | + String getKey(); | |
| 15 | + | |
| 16 | + /** | |
| 17 | + * 根据用户 ID 返回统计数量 | |
| 18 | + */ | |
| 19 | + Integer getCount(Long userId); | |
| 20 | +} | |
| 0 | 21 | \ No newline at end of file | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/appmodule/statistics/StatisticsProviderRegistry.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.service.appmodule.statistics; | |
| 2 | + | |
| 3 | +import org.springframework.stereotype.Component; | |
| 4 | + | |
| 5 | +import java.util.List; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * 统计提供者路由器 | |
| 9 | + * | |
| 10 | + * 收集所有 {@link StatisticProvider} 实现,并按 key 路由调用。 | |
| 11 | + */ | |
| 12 | +@Component | |
| 13 | +public class StatisticsProviderRegistry { | |
| 14 | + | |
| 15 | + private final List<StatisticProvider> providers; | |
| 16 | + | |
| 17 | + public StatisticsProviderRegistry(List<StatisticProvider> providers) { | |
| 18 | + this.providers = providers; | |
| 19 | + } | |
| 20 | + | |
| 21 | + /** | |
| 22 | + * 根据 statisticKey 查找提供者并返回统计数;找不到或异常时返回 0。 | |
| 23 | + */ | |
| 24 | + public Integer getCount(String statisticKey, Long userId) { | |
| 25 | + if (providers == null || providers.isEmpty()) { | |
| 26 | + return 0; | |
| 27 | + } | |
| 28 | + for (StatisticProvider provider : providers) { | |
| 29 | + if (provider != null && statisticKey != null && statisticKey.equals(provider.getKey())) { | |
| 30 | + try { | |
| 31 | + Integer count = provider.getCount(userId); | |
| 32 | + return count != null ? count : 0; | |
| 33 | + } catch (Exception ignored) { | |
| 34 | + return 0; | |
| 35 | + } | |
| 36 | + } | |
| 37 | + } | |
| 38 | + return 0; | |
| 39 | + } | |
| 40 | +} | |
| 0 | 41 | \ No newline at end of file | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/appmodule/strategy/GrayReleaseStrategy.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.service.appmodule.strategy; | |
| 2 | + | |
| 3 | +import java.util.Map; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * 灰度发布策略接口 | |
| 7 | + * | |
| 8 | + * @author system | |
| 9 | + */ | |
| 10 | +public interface GrayReleaseStrategy { | |
| 11 | + | |
| 12 | + /** | |
| 13 | + * 判断用户是否命中灰度规则 | |
| 14 | + * | |
| 15 | + * @param config 灰度配置 | |
| 16 | + * @param userId 用户ID | |
| 17 | + * @return 是否命中 | |
| 18 | + */ | |
| 19 | + boolean isMatch(Map<String, Object> config, Long userId); | |
| 20 | + | |
| 21 | + /** | |
| 22 | + * 获取策略类型 | |
| 23 | + * | |
| 24 | + * @return 策略类型 | |
| 25 | + */ | |
| 26 | + String getType(); | |
| 27 | + | |
| 28 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/appmodule/strategy/GrayReleaseStrategyFactory.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.service.appmodule.strategy; | |
| 2 | + | |
| 3 | +import jakarta.annotation.PostConstruct; | |
| 4 | +import jakarta.annotation.Resource; | |
| 5 | +import org.springframework.stereotype.Component; | |
| 6 | + | |
| 7 | +import java.util.HashMap; | |
| 8 | +import java.util.List; | |
| 9 | +import java.util.Map; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * 灰度发布策略工厂 | |
| 13 | + * | |
| 14 | + * @author system | |
| 15 | + */ | |
| 16 | +@Component | |
| 17 | +public class GrayReleaseStrategyFactory { | |
| 18 | + | |
| 19 | + @Resource | |
| 20 | + private List<GrayReleaseStrategy> strategies; | |
| 21 | + | |
| 22 | + private final Map<String, GrayReleaseStrategy> strategyMap = new HashMap<>(); | |
| 23 | + | |
| 24 | + @PostConstruct | |
| 25 | + public void init() { | |
| 26 | + for (GrayReleaseStrategy strategy : strategies) { | |
| 27 | + strategyMap.put(strategy.getType(), strategy); | |
| 28 | + } | |
| 29 | + // userIds 类型使用相同的白名单策略 | |
| 30 | + strategyMap.put("userIds", strategyMap.get("whitelist")); | |
| 31 | + } | |
| 32 | + | |
| 33 | + /** | |
| 34 | + * 获取策略 | |
| 35 | + * | |
| 36 | + * @param type 策略类型 | |
| 37 | + * @return 策略实例 | |
| 38 | + */ | |
| 39 | + public GrayReleaseStrategy getStrategy(String type) { | |
| 40 | + return strategyMap.get(type); | |
| 41 | + } | |
| 42 | + | |
| 43 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/appmodule/strategy/VisibleScopeStrategy.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.service.appmodule.strategy; | |
| 2 | + | |
| 3 | + | |
| 4 | +import com.zteits.urbanops.module.system.dal.dataobject.appmodule.AppModuleDO; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * 可见范围策略接口 | |
| 8 | + * | |
| 9 | + * @author system | |
| 10 | + */ | |
| 11 | +public interface VisibleScopeStrategy { | |
| 12 | + | |
| 13 | + /** | |
| 14 | + * 判断模块对用户是否可见 | |
| 15 | + * | |
| 16 | + * @param module 模块信息 | |
| 17 | + * @param userId 用户ID | |
| 18 | + * @return 是否可见 | |
| 19 | + */ | |
| 20 | + boolean isVisible(AppModuleDO module, Long userId); | |
| 21 | + | |
| 22 | + /** | |
| 23 | + * 获取策略类型 | |
| 24 | + * | |
| 25 | + * @return 策略类型 | |
| 26 | + */ | |
| 27 | + Integer getType(); | |
| 28 | + | |
| 29 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/appmodule/strategy/VisibleScopeStrategyFactory.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.service.appmodule.strategy; | |
| 2 | + | |
| 3 | +import jakarta.annotation.PostConstruct; | |
| 4 | +import jakarta.annotation.Resource; | |
| 5 | +import org.springframework.stereotype.Component; | |
| 6 | + | |
| 7 | +import java.util.HashMap; | |
| 8 | +import java.util.List; | |
| 9 | +import java.util.Map; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * 可见范围策略工厂 | |
| 13 | + * | |
| 14 | + * @author system | |
| 15 | + */ | |
| 16 | +@Component | |
| 17 | +public class VisibleScopeStrategyFactory { | |
| 18 | + | |
| 19 | + @Resource | |
| 20 | + private List<VisibleScopeStrategy> strategies; | |
| 21 | + | |
| 22 | + private final Map<Integer, VisibleScopeStrategy> strategyMap = new HashMap<>(); | |
| 23 | + | |
| 24 | + @PostConstruct | |
| 25 | + public void init() { | |
| 26 | + for (VisibleScopeStrategy strategy : strategies) { | |
| 27 | + strategyMap.put(strategy.getType(), strategy); | |
| 28 | + } | |
| 29 | + } | |
| 30 | + | |
| 31 | + /** | |
| 32 | + * 获取策略 | |
| 33 | + * | |
| 34 | + * @param type 策略类型 | |
| 35 | + * @return 策略实例 | |
| 36 | + */ | |
| 37 | + public VisibleScopeStrategy getStrategy(Integer type) { | |
| 38 | + return strategyMap.get(type); | |
| 39 | + } | |
| 40 | + | |
| 41 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/appmodule/strategy/impl/AllVisibleStrategy.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.service.appmodule.strategy.impl; | |
| 2 | + | |
| 3 | +import com.zteits.urbanops.module.system.dal.dataobject.appmodule.AppModuleDO; | |
| 4 | +import com.zteits.urbanops.module.system.enums.appmodule.AppModuleVisibleScopeEnum; | |
| 5 | +import com.zteits.urbanops.module.system.service.appmodule.strategy.VisibleScopeStrategy; | |
| 6 | +import org.springframework.stereotype.Component; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * 所有用户可见策略 | |
| 10 | + * | |
| 11 | + * @author system | |
| 12 | + */ | |
| 13 | +@Component | |
| 14 | +public class AllVisibleStrategy implements VisibleScopeStrategy { | |
| 15 | + | |
| 16 | + @Override | |
| 17 | + public boolean isVisible(AppModuleDO module, Long userId) { | |
| 18 | + return true; | |
| 19 | + } | |
| 20 | + | |
| 21 | + @Override | |
| 22 | + public Integer getType() { | |
| 23 | + return AppModuleVisibleScopeEnum.ALL.getScope(); | |
| 24 | + } | |
| 25 | + | |
| 26 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/appmodule/strategy/impl/PercentGrayStrategy.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.service.appmodule.strategy.impl; | |
| 2 | + | |
| 3 | +import com.zteits.urbanops.module.system.service.appmodule.strategy.GrayReleaseStrategy; | |
| 4 | +import lombok.extern.slf4j.Slf4j; | |
| 5 | +import org.springframework.stereotype.Component; | |
| 6 | + | |
| 7 | +import java.util.Map; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * 百分比灰度策略 | |
| 11 | + * | |
| 12 | + * @author system | |
| 13 | + */ | |
| 14 | +@Component | |
| 15 | +@Slf4j | |
| 16 | +public class PercentGrayStrategy implements GrayReleaseStrategy { | |
| 17 | + | |
| 18 | + @Override | |
| 19 | + public boolean isMatch(Map<String, Object> config, Long userId) { | |
| 20 | + try { | |
| 21 | + Integer percent = (Integer) config.get("value"); | |
| 22 | + return userId % 100 < percent; | |
| 23 | + } catch (Exception e) { | |
| 24 | + log.error("[百分比灰度策略] 解析配置失败. config={}", config, e); | |
| 25 | + return false; | |
| 26 | + } | |
| 27 | + } | |
| 28 | + | |
| 29 | + @Override | |
| 30 | + public String getType() { | |
| 31 | + return "percent"; | |
| 32 | + } | |
| 33 | + | |
| 34 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/appmodule/strategy/impl/RoleVisibleStrategy.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.service.appmodule.strategy.impl; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.util.StrUtil; | |
| 5 | +import com.zteits.urbanops.module.system.dal.dataobject.appmodule.AppModuleDO; | |
| 6 | +import com.zteits.urbanops.module.system.dal.dataobject.permission.RoleDO; | |
| 7 | +import com.zteits.urbanops.module.system.enums.appmodule.AppModuleVisibleScopeEnum; | |
| 8 | +import com.zteits.urbanops.module.system.service.appmodule.strategy.VisibleScopeStrategy; | |
| 9 | +import com.zteits.urbanops.module.system.service.permission.PermissionService; | |
| 10 | +import com.zteits.urbanops.module.system.service.permission.RoleService; | |
| 11 | +import lombok.RequiredArgsConstructor; | |
| 12 | +import lombok.extern.slf4j.Slf4j; | |
| 13 | +import org.springframework.stereotype.Component; | |
| 14 | + | |
| 15 | +import java.util.List; | |
| 16 | +import java.util.Set; | |
| 17 | +import java.util.stream.Collectors; | |
| 18 | + | |
| 19 | +/** | |
| 20 | + * 指定角色编码可见策略 | |
| 21 | + */ | |
| 22 | +@Component | |
| 23 | +@Slf4j | |
| 24 | +@RequiredArgsConstructor | |
| 25 | +public class RoleVisibleStrategy implements VisibleScopeStrategy { | |
| 26 | + | |
| 27 | + private final PermissionService permissionService; | |
| 28 | + private final RoleService roleService; | |
| 29 | + | |
| 30 | + @Override | |
| 31 | + public boolean isVisible(AppModuleDO module, Long userId) { | |
| 32 | + if (userId == null) { | |
| 33 | + return false; | |
| 34 | + } | |
| 35 | + List<?> visibleConfig = module.getVisibleConfig(); | |
| 36 | + if (CollUtil.isEmpty(visibleConfig)) { | |
| 37 | + return false; | |
| 38 | + } | |
| 39 | + | |
| 40 | + Set<Long> userRoleIds = permissionService.getUserRoleIdListByUserIdFromCache(userId); | |
| 41 | + if (CollUtil.isEmpty(userRoleIds)) { | |
| 42 | + return false; | |
| 43 | + } | |
| 44 | + | |
| 45 | + List<RoleDO> roles = roleService.getRoleListFromCache(userRoleIds); | |
| 46 | + if (CollUtil.isEmpty(roles)) { | |
| 47 | + return false; | |
| 48 | + } | |
| 49 | + | |
| 50 | + Set<String> userRoleCodes = roles.stream() | |
| 51 | + .map(RoleDO::getCode) | |
| 52 | + .filter(StrUtil::isNotBlank) | |
| 53 | + .collect(Collectors.toSet()); | |
| 54 | + if (CollUtil.isEmpty(userRoleCodes)) { | |
| 55 | + return false; | |
| 56 | + } | |
| 57 | + | |
| 58 | + for (Object configValue : visibleConfig) { | |
| 59 | + String requiredCode = convertToCode(configValue); | |
| 60 | + if (StrUtil.isBlank(requiredCode)) { | |
| 61 | + continue; | |
| 62 | + } | |
| 63 | + if (userRoleCodes.contains(requiredCode)) { | |
| 64 | + return true; | |
| 65 | + } | |
| 66 | + } | |
| 67 | + return false; | |
| 68 | + } | |
| 69 | + | |
| 70 | + private String convertToCode(Object configValue) { | |
| 71 | + if (configValue == null) { | |
| 72 | + return null; | |
| 73 | + } | |
| 74 | + return StrUtil.trimToNull(configValue.toString()); | |
| 75 | + } | |
| 76 | + | |
| 77 | + @Override | |
| 78 | + public Integer getType() { | |
| 79 | + return AppModuleVisibleScopeEnum.ROLE_ID.getScope(); | |
| 80 | + } | |
| 81 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/appmodule/strategy/impl/UserIdVisibleStrategy.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.service.appmodule.strategy.impl; | |
| 2 | + | |
| 3 | +import com.zteits.urbanops.module.system.dal.dataobject.appmodule.AppModuleDO; | |
| 4 | +import com.zteits.urbanops.module.system.enums.appmodule.AppModuleVisibleScopeEnum; | |
| 5 | +import com.zteits.urbanops.module.system.service.appmodule.strategy.VisibleScopeStrategy; | |
| 6 | +import lombok.extern.slf4j.Slf4j; | |
| 7 | +import org.springframework.stereotype.Component; | |
| 8 | +import org.springframework.util.CollectionUtils; | |
| 9 | + | |
| 10 | +import java.util.List; | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * 指定用户ID可见策略 | |
| 14 | + * | |
| 15 | + * @author system | |
| 16 | + */ | |
| 17 | +@Component | |
| 18 | +@Slf4j | |
| 19 | +public class UserIdVisibleStrategy implements VisibleScopeStrategy { | |
| 20 | + | |
| 21 | + @Override | |
| 22 | + public boolean isVisible(AppModuleDO module, Long userId) { | |
| 23 | + List<?> visibleConfig = module.getVisibleConfig(); | |
| 24 | + if (CollectionUtils.isEmpty(visibleConfig)) { | |
| 25 | + return false; | |
| 26 | + } | |
| 27 | + | |
| 28 | + try { | |
| 29 | + // 将userId转为字符串 | |
| 30 | + String userIdStr = String.valueOf(userId); | |
| 31 | + | |
| 32 | + // 检查visibleConfig中是否有元素转换为字符串后与userIdStr相等 | |
| 33 | + return visibleConfig.stream() | |
| 34 | + .map(String::valueOf) | |
| 35 | + .anyMatch(userIdStr::equals); | |
| 36 | + } catch (Exception e) { | |
| 37 | + log.error("[用户ID可见策略] 解析配置失败. config={}", visibleConfig, e); | |
| 38 | + return false; | |
| 39 | + } | |
| 40 | + } | |
| 41 | + | |
| 42 | + @Override | |
| 43 | + public Integer getType() { | |
| 44 | + return AppModuleVisibleScopeEnum.USER_ID.getScope(); | |
| 45 | + } | |
| 46 | + | |
| 47 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/appmodule/strategy/impl/UserTagVisibleStrategy.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.service.appmodule.strategy.impl; | |
| 2 | + | |
| 3 | +import com.zteits.urbanops.module.system.dal.dataobject.appmodule.AppModuleDO; | |
| 4 | +import com.zteits.urbanops.module.system.enums.appmodule.AppModuleVisibleScopeEnum; | |
| 5 | +import com.zteits.urbanops.module.system.service.appmodule.strategy.VisibleScopeStrategy; | |
| 6 | +import lombok.extern.slf4j.Slf4j; | |
| 7 | +import org.springframework.stereotype.Component; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * 指定用户标签可见策略 | |
| 11 | + * | |
| 12 | + * @author system | |
| 13 | + */ | |
| 14 | +@Component | |
| 15 | +@Slf4j | |
| 16 | +public class UserTagVisibleStrategy implements VisibleScopeStrategy { | |
| 17 | + | |
| 18 | + @Override | |
| 19 | + public boolean isVisible(AppModuleDO module, Long userId) { | |
| 20 | + // TODO: 根据实际业务获取用户标签,这里是示例实现 | |
| 21 | + // List<String> userTags = userService.getUserTags(userId); | |
| 22 | + // String visibleConfig = module.getVisibleConfig(); | |
| 23 | + // if (StrUtil.isBlank(visibleConfig)) { | |
| 24 | + // return false; | |
| 25 | + // } | |
| 26 | + // try { | |
| 27 | + // List<String> allowedTags = JSONUtil.toList(visibleConfig, String.class); | |
| 28 | + // return CollUtil.containsAny(userTags, allowedTags); | |
| 29 | + // } catch (Exception e) { | |
| 30 | + // log.error("[用户标签可见策略] 解析配置失败. config={}", visibleConfig, e); | |
| 31 | + // return false; | |
| 32 | + // } | |
| 33 | + | |
| 34 | + log.warn("[用户标签可见策略] 待实现,默认返回 true. userId={}", userId); | |
| 35 | + return true; | |
| 36 | + } | |
| 37 | + | |
| 38 | + @Override | |
| 39 | + public Integer getType() { | |
| 40 | + return AppModuleVisibleScopeEnum.USER_TAG.getScope(); | |
| 41 | + } | |
| 42 | + | |
| 43 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/appmodule/strategy/impl/UserTypeVisibleStrategy.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.service.appmodule.strategy.impl; | |
| 2 | + | |
| 3 | +import com.zteits.urbanops.module.system.dal.dataobject.appmodule.AppModuleDO; | |
| 4 | +import com.zteits.urbanops.module.system.enums.appmodule.AppModuleVisibleScopeEnum; | |
| 5 | +import com.zteits.urbanops.module.system.service.appmodule.strategy.VisibleScopeStrategy; | |
| 6 | +import lombok.extern.slf4j.Slf4j; | |
| 7 | +import org.springframework.stereotype.Component; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * 指定用户类型可见策略 | |
| 11 | + * | |
| 12 | + * @author system | |
| 13 | + */ | |
| 14 | +@Component | |
| 15 | +@Slf4j | |
| 16 | +public class UserTypeVisibleStrategy implements VisibleScopeStrategy { | |
| 17 | + | |
| 18 | + @Override | |
| 19 | + public boolean isVisible(AppModuleDO module, Long userId) { | |
| 20 | + // TODO: 根据实际业务获取用户类型,这里是示例实现 | |
| 21 | + // String userType = userService.getUserType(userId); | |
| 22 | + // String visibleConfig = module.getVisibleConfig(); | |
| 23 | + // if (StrUtil.isBlank(visibleConfig)) { | |
| 24 | + // return false; | |
| 25 | + // } | |
| 26 | + // try { | |
| 27 | + // List<String> allowedTypes = JSONUtil.toList(visibleConfig, String.class); | |
| 28 | + // return allowedTypes.contains(userType); | |
| 29 | + // } catch (Exception e) { | |
| 30 | + // log.error("[用户类型可见策略] 解析配置失败. config={}", visibleConfig, e); | |
| 31 | + // return false; | |
| 32 | + // } | |
| 33 | + | |
| 34 | + log.warn("[用户类型可见策略] 待实现,默认返回 true. userId={}", userId); | |
| 35 | + return true; | |
| 36 | + } | |
| 37 | + | |
| 38 | + @Override | |
| 39 | + public Integer getType() { | |
| 40 | + return AppModuleVisibleScopeEnum.USER_TYPE.getScope(); | |
| 41 | + } | |
| 42 | + | |
| 43 | +} | ... | ... |
urbanops-module-system/src/main/java/com/zteits/urbanops/module/system/service/appmodule/strategy/impl/WhitelistGrayStrategy.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.system.service.appmodule.strategy.impl; | |
| 2 | + | |
| 3 | +import cn.hutool.json.JSONUtil; | |
| 4 | +import com.zteits.urbanops.module.system.service.appmodule.strategy.GrayReleaseStrategy; | |
| 5 | +import lombok.extern.slf4j.Slf4j; | |
| 6 | +import org.springframework.stereotype.Component; | |
| 7 | + | |
| 8 | +import java.util.List; | |
| 9 | +import java.util.Map; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * 白名单灰度策略 | |
| 13 | + * | |
| 14 | + * @author system | |
| 15 | + */ | |
| 16 | +@Component | |
| 17 | +@Slf4j | |
| 18 | +public class WhitelistGrayStrategy implements GrayReleaseStrategy { | |
| 19 | + | |
| 20 | + @Override | |
| 21 | + public boolean isMatch(Map<String, Object> config, Long userId) { | |
| 22 | + try { | |
| 23 | + List<Long> userIds = JSONUtil.toList(JSONUtil.toJsonStr(config.get("value")), Long.class); | |
| 24 | + return userIds.contains(userId); | |
| 25 | + } catch (Exception e) { | |
| 26 | + log.error("[白名单灰度策略] 解析配置失败. config={}", config, e); | |
| 27 | + return false; | |
| 28 | + } | |
| 29 | + } | |
| 30 | + | |
| 31 | + @Override | |
| 32 | + public String getType() { | |
| 33 | + return "whitelist"; | |
| 34 | + } | |
| 35 | + | |
| 36 | +} | ... | ... |