Commit 16a4bc710f7f052e372df68c46bbee25107e097c

Authored by 戈灵虎
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,7 +132,8 @@ public class WebFrameworkUtils {
132 return UserTypeEnum.ADMIN.getValue(); 132 return UserTypeEnum.ADMIN.getValue();
133 } 133 }
134 if (request.getServletPath().startsWith(properties.getAppApi().getPrefix())) { 134 if (request.getServletPath().startsWith(properties.getAppApi().getPrefix())) {
135 - return UserTypeEnum.MEMBER.getValue(); 135 + // 20251206 我改的,目前前后端用户同一套,不做区分
  136 + return UserTypeEnum.ADMIN.getValue();
136 } 137 }
137 return null; 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,7 +98,8 @@ public class OldtreesController {
98 @TransMethodResult 98 @TransMethodResult
99 public CommonResult<OldtreesRespVO> getOldtrees(@RequestParam("id") String id) { 99 public CommonResult<OldtreesRespVO> getOldtrees(@RequestParam("id") String id) {
100 OldtreesDO oldtrees = oldtreesService.getOldtrees(id); 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 @GetMapping({"/page","/list"}) 105 @GetMapping({"/page","/list"})
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/oldtrees/vo/OldtreesRespVO.java
1 package com.zteits.urbanops.module.garden.controller.admin.oldtrees.vo; 1 package com.zteits.urbanops.module.garden.controller.admin.oldtrees.vo;
2 2
3 -import com.baomidou.mybatisplus.annotation.TableField;  
4 import com.fasterxml.jackson.annotation.JsonFormat; 3 import com.fasterxml.jackson.annotation.JsonFormat;
5 import com.fasterxml.jackson.databind.annotation.JsonSerialize; 4 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
6 import com.fhs.core.trans.anno.Trans; 5 import com.fhs.core.trans.anno.Trans;
7 import com.fhs.core.trans.constant.TransType; 6 import com.fhs.core.trans.constant.TransType;
  7 +import com.fhs.core.trans.vo.VO;
8 import com.zteits.urbanops.framework.common.util.json.databind.LocalDateTimeStringSerializer; 8 import com.zteits.urbanops.framework.common.util.json.databind.LocalDateTimeStringSerializer;
9 import com.zteits.urbanops.module.system.dal.dataobject.dept.DeptDO; 9 import com.zteits.urbanops.module.system.dal.dataobject.dept.DeptDO;
10 import com.zteits.urbanops.module.system.dal.dataobject.user.AdminUserDO; 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,7 +21,7 @@ import static com.zteits.urbanops.framework.common.util.date.DateUtils.TIME_ZONE
21 @Schema(description = "管理后台 - 古树名木 Response VO") 21 @Schema(description = "管理后台 - 古树名木 Response VO")
22 @Data 22 @Data
23 @ExcelIgnoreUnannotated 23 @ExcelIgnoreUnannotated
24 -public class OldtreesRespVO { 24 +public class OldtreesRespVO implements VO {
25 25
26 @Schema(description = "id", example = "4258") 26 @Schema(description = "id", example = "4258")
27 // @ExcelProperty("id") 27 // @ExcelProperty("id")
@@ -43,9 +43,13 @@ public class OldtreesRespVO { @@ -43,9 +43,13 @@ public class OldtreesRespVO {
43 @ExcelProperty("所在地") 43 @ExcelProperty("所在地")
44 private String location; 44 private String location;
45 45
  46 + @Trans(type = TransType.SIMPLE, target = DeptDO.class, fields = "name",ref = "maintainunitName")
  47 + private String maintainunit;
  48 +
46 @Schema(description = "养护单位") 49 @Schema(description = "养护单位")
47 @ExcelProperty("养护单位") 50 @ExcelProperty("养护单位")
48 - private String maintainunit; 51 + private String maintainunitName;
  52 +
49 53
50 @Schema(description = "权属分类--数据字典:专业绿地和单位(居住区)") 54 @Schema(description = "权属分类--数据字典:专业绿地和单位(居住区)")
51 @ExcelProperty("权属分类") 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,10 +47,12 @@ public class OldtreesChangeLogRespVO implements VO {
47 @ExcelProperty("所在地") 47 @ExcelProperty("所在地")
48 private String location; 48 private String location;
49 49
  50 + @Trans(type = TransType.SIMPLE, target = DeptDO.class, fields = "name",ref = "maintainunitName")
  51 + private String maintainunit;
  52 +
50 @Schema(description = "养护单位") 53 @Schema(description = "养护单位")
51 @ExcelProperty("养护单位") 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 @Schema(description = "权属分类--数据字典:专业绿地和单位(居住区)") 57 @Schema(description = "权属分类--数据字典:专业绿地和单位(居住区)")
56 @ExcelProperty("权属分类--数据字典:专业绿地和单位(居住区)") 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,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
5 import com.fasterxml.jackson.databind.annotation.JsonSerialize; 5 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
6 import com.fhs.core.trans.anno.Trans; 6 import com.fhs.core.trans.anno.Trans;
7 import com.fhs.core.trans.constant.TransType; 7 import com.fhs.core.trans.constant.TransType;
  8 +import com.fhs.core.trans.vo.VO;
8 import com.zteits.urbanops.framework.common.util.json.databind.LocalDateTimeStringSerializer; 9 import com.zteits.urbanops.framework.common.util.json.databind.LocalDateTimeStringSerializer;
9 import com.zteits.urbanops.module.system.dal.dataobject.dept.DeptDO; 10 import com.zteits.urbanops.module.system.dal.dataobject.dept.DeptDO;
10 import com.zteits.urbanops.module.system.dal.dataobject.user.AdminUserDO; 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,7 +22,7 @@ import static com.zteits.urbanops.framework.common.util.date.DateUtils.TIME_ZONE
21 @Schema(description = "管理后台 - 一树一档案 Response VO") 22 @Schema(description = "管理后台 - 一树一档案 Response VO")
22 @Data 23 @Data
23 @ExcelIgnoreUnannotated 24 @ExcelIgnoreUnannotated
24 -public class TreeRespVO { 25 +public class TreeRespVO implements VO {
25 26
26 @Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "28236") 27 @Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "28236")
27 // @ExcelProperty("id") 28 // @ExcelProperty("id")
@@ -71,9 +72,13 @@ public class TreeRespVO { @@ -71,9 +72,13 @@ public class TreeRespVO {
71 // @ExcelProperty("所在地") 72 // @ExcelProperty("所在地")
72 private String location; 73 private String location;
73 74
  75 + @Trans(type = TransType.SIMPLE, target = DeptDO.class, fields = "name", ref = "maintainunitName")
  76 + private String maintainunit;
  77 +
74 @Schema(description = "养护单位") 78 @Schema(description = "养护单位")
75 @ExcelProperty("养护单位") 79 @ExcelProperty("养护单位")
76 - private String maintainunit; 80 + private String maintainunitName;
  81 +
77 82
78 @Schema(description = "管护单位") 83 @Schema(description = "管护单位")
79 @ExcelProperty("管护单位") 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,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
4 import com.fasterxml.jackson.databind.annotation.JsonSerialize; 4 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
5 import com.fhs.core.trans.anno.Trans; 5 import com.fhs.core.trans.anno.Trans;
6 import com.fhs.core.trans.constant.TransType; 6 import com.fhs.core.trans.constant.TransType;
  7 +import com.fhs.core.trans.vo.VO;
7 import com.zteits.urbanops.framework.common.util.json.databind.LocalDateTimeStringSerializer; 8 import com.zteits.urbanops.framework.common.util.json.databind.LocalDateTimeStringSerializer;
8 import com.zteits.urbanops.module.system.dal.dataobject.dept.DeptDO; 9 import com.zteits.urbanops.module.system.dal.dataobject.dept.DeptDO;
9 import com.zteits.urbanops.module.system.dal.dataobject.user.AdminUserDO; 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,7 +21,7 @@ import static com.zteits.urbanops.framework.common.util.date.DateUtils.TIME_ZONE
20 @Schema(description = "管理后台 - 一树一档案修改记录 Response VO") 21 @Schema(description = "管理后台 - 一树一档案修改记录 Response VO")
21 @Data 22 @Data
22 @ExcelIgnoreUnannotated 23 @ExcelIgnoreUnannotated
23 -public class TreeChangeLogRespVO { 24 +public class TreeChangeLogRespVO implements VO {
24 25
25 @Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "3528") 26 @Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "3528")
26 @ExcelProperty("id") 27 @ExcelProperty("id")
@@ -74,10 +75,12 @@ public class TreeChangeLogRespVO { @@ -74,10 +75,12 @@ public class TreeChangeLogRespVO {
74 @ExcelProperty("所在地") 75 @ExcelProperty("所在地")
75 private String location; 76 private String location;
76 77
  78 + @Trans(type = TransType.SIMPLE, target = DeptDO.class, fields = "name",ref = "maintainunitName")
  79 + private String maintainunit;
  80 +
77 @Schema(description = "养护单位") 81 @Schema(description = "养护单位")
78 @ExcelProperty("养护单位") 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 @Schema(description = "管护单位") 85 @Schema(description = "管护单位")
83 @ExcelProperty("管护单位") 86 @ExcelProperty("管护单位")
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/dataobject/oldtrees/OldtreesDO.java
@@ -47,6 +47,9 @@ public class OldtreesDO { @@ -47,6 +47,9 @@ public class OldtreesDO {
47 * 养护单位 47 * 养护单位
48 */ 48 */
49 private String maintainunit; 49 private String maintainunit;
  50 +
  51 + @TableField(exist = false)
  52 + private String maintainunitName;
50 /** 53 /**
51 * 权属分类--数据字典:专业绿地和单位(居住区) 54 * 权属分类--数据字典:专业绿地和单位(居住区)
52 */ 55 */
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/dataobject/oldtreeschangelog/OldtreesChangeLogDO.java
@@ -52,6 +52,9 @@ public class OldtreesChangeLogDO { @@ -52,6 +52,9 @@ public class OldtreesChangeLogDO {
52 * 养护单位 52 * 养护单位
53 */ 53 */
54 private String maintainunit; 54 private String maintainunit;
  55 +
  56 + @TableField(exist = false)
  57 + private String maintainunitName;
55 /** 58 /**
56 * 权属分类--数据字典:专业绿地和单位(居住区) 59 * 权属分类--数据字典:专业绿地和单位(居住区)
57 */ 60 */
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/dataobject/tree/TreeDO.java
@@ -77,6 +77,9 @@ public class TreeDO { @@ -77,6 +77,9 @@ public class TreeDO {
77 * 养护单位 77 * 养护单位
78 */ 78 */
79 private String maintainunit; 79 private String maintainunit;
  80 +
  81 + @TableField(exist = false)
  82 + private String maintainunitName;
80 /** 83 /**
81 * 管护单位 84 * 管护单位
82 */ 85 */
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/dataobject/treechangelog/TreeChangeLogDO.java
@@ -80,6 +80,9 @@ public class TreeChangeLogDO { @@ -80,6 +80,9 @@ public class TreeChangeLogDO {
80 * 养护单位 80 * 养护单位
81 */ 81 */
82 private String maintainunit; 82 private String maintainunit;
  83 +
  84 + @TableField(exist = false)
  85 + private String maintainunitName;
83 /** 86 /**
84 * 管护单位 87 * 管护单位
85 */ 88 */
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&lt;OldtreesDO&gt; { @@ -22,7 +22,7 @@ public interface OldtreesMapper extends BaseMapperX&lt;OldtreesDO&gt; {
22 22
23 default PageResult<OldtreesDO> selectPage(OldtreesPageReqVO reqVO) { 23 default PageResult<OldtreesDO> selectPage(OldtreesPageReqVO reqVO) {
24 MPJLambdaWrapperX<OldtreesDO> wrapper = new MPJLambdaWrapperX<OldtreesDO>() 24 MPJLambdaWrapperX<OldtreesDO> wrapper = new MPJLambdaWrapperX<OldtreesDO>()
25 - .selectAs(DeptDO::getName, OldtreesDO::getMaintainunit) 25 + .selectAs(DeptDO::getName, OldtreesDO::getMaintainunitName)
26 .selectAll(OldtreesDO.class) 26 .selectAll(OldtreesDO.class)
27 .leftJoin(DeptDO.class, DeptDO::getId, OldtreesDO::getMaintainunit) 27 .leftJoin(DeptDO.class, DeptDO::getId, OldtreesDO::getMaintainunit)
28 .eqIfPresent(OldtreesDO::getFamoustreenumber, reqVO.getFamoustreenumber()) 28 .eqIfPresent(OldtreesDO::getFamoustreenumber, reqVO.getFamoustreenumber())
@@ -66,6 +66,7 @@ public interface OldtreesMapper extends BaseMapperX&lt;OldtreesDO&gt; { @@ -66,6 +66,7 @@ public interface OldtreesMapper extends BaseMapperX&lt;OldtreesDO&gt; {
66 .eqIfPresent(OldtreesDO::getUpdateby, reqVO.getUpdateby()) 66 .eqIfPresent(OldtreesDO::getUpdateby, reqVO.getUpdateby())
67 .betweenIfPresent(OldtreesDO::getUpdatetime, reqVO.getUpdatetime()) 67 .betweenIfPresent(OldtreesDO::getUpdatetime, reqVO.getUpdatetime())
68 .eqIfPresent(OldtreesDO::getDeptId, reqVO.getDeptId()) 68 .eqIfPresent(OldtreesDO::getDeptId, reqVO.getDeptId())
  69 + .orderByDesc(OldtreesDO::getUpdatetime)
69 .orderByDesc(OldtreesDO::getCreatetime); 70 .orderByDesc(OldtreesDO::getCreatetime);
70 71
71 return selectJoinPage(reqVO, OldtreesDO.class, wrapper); 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&lt;OldtreesChangeLogDO @@ -22,7 +22,7 @@ public interface OldtreesChangeLogMapper extends BaseMapperX&lt;OldtreesChangeLogDO
22 default PageResult<OldtreesChangeLogDO> selectPage(OldtreesChangeLogPageReqVO reqVO) { 22 default PageResult<OldtreesChangeLogDO> selectPage(OldtreesChangeLogPageReqVO reqVO) {
23 23
24 MPJLambdaWrapperX<OldtreesChangeLogDO> wrapper = new MPJLambdaWrapperX<OldtreesChangeLogDO>() 24 MPJLambdaWrapperX<OldtreesChangeLogDO> wrapper = new MPJLambdaWrapperX<OldtreesChangeLogDO>()
25 - .selectAs(DeptDO::getName, OldtreesChangeLogDO::getMaintainunit) 25 + .selectAs(DeptDO::getName, OldtreesChangeLogDO::getMaintainunitName)
26 .selectAll(OldtreesChangeLogDO.class) 26 .selectAll(OldtreesChangeLogDO.class)
27 .leftJoin(DeptDO.class, DeptDO::getId, OldtreesChangeLogDO::getMaintainunit) 27 .leftJoin(DeptDO.class, DeptDO::getId, OldtreesChangeLogDO::getMaintainunit)
28 .eqIfPresent(OldtreesChangeLogDO::getOldtreeid, reqVO.getOldtreeid()) 28 .eqIfPresent(OldtreesChangeLogDO::getOldtreeid, reqVO.getOldtreeid())
@@ -66,6 +66,7 @@ public interface OldtreesChangeLogMapper extends BaseMapperX&lt;OldtreesChangeLogDO @@ -66,6 +66,7 @@ public interface OldtreesChangeLogMapper extends BaseMapperX&lt;OldtreesChangeLogDO
66 .betweenIfPresent(OldtreesChangeLogDO::getCreatetime, reqVO.getCreatetime()) 66 .betweenIfPresent(OldtreesChangeLogDO::getCreatetime, reqVO.getCreatetime())
67 .eqIfPresent(OldtreesChangeLogDO::getUpdateby, reqVO.getUpdateby()) 67 .eqIfPresent(OldtreesChangeLogDO::getUpdateby, reqVO.getUpdateby())
68 .betweenIfPresent(OldtreesChangeLogDO::getUpdatetime, reqVO.getUpdatetime()) 68 .betweenIfPresent(OldtreesChangeLogDO::getUpdatetime, reqVO.getUpdatetime())
  69 + .orderByDesc(OldtreesChangeLogDO::getUpdatetime)
69 .orderByDesc(OldtreesChangeLogDO::getCreatetime); 70 .orderByDesc(OldtreesChangeLogDO::getCreatetime);
70 71
71 return selectJoinPage(reqVO, OldtreesChangeLogDO.class, wrapper); 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&lt;TreeDO&gt; { @@ -22,7 +22,7 @@ public interface TreeMapper extends BaseMapperX&lt;TreeDO&gt; {
22 default PageResult<TreeDO> selectPage(TreePageReqVO reqVO) { 22 default PageResult<TreeDO> selectPage(TreePageReqVO reqVO) {
23 23
24 MPJLambdaWrapperX<TreeDO> wrapper = new MPJLambdaWrapperX<TreeDO>() 24 MPJLambdaWrapperX<TreeDO> wrapper = new MPJLambdaWrapperX<TreeDO>()
25 - .selectAs(DeptDO::getName, TreeDO::getMaintainunit) 25 + .selectAs(DeptDO::getName, TreeDO::getMaintainunitName)
26 .selectAll(TreeDO.class) 26 .selectAll(TreeDO.class)
27 .leftJoin(DeptDO.class, DeptDO::getId, TreeDO::getMaintainunit) 27 .leftJoin(DeptDO.class, DeptDO::getId, TreeDO::getMaintainunit)
28 .eqIfPresent(TreeDO::getTreenumber, reqVO.getTreenumber()) 28 .eqIfPresent(TreeDO::getTreenumber, reqVO.getTreenumber())
@@ -58,6 +58,7 @@ public interface TreeMapper extends BaseMapperX&lt;TreeDO&gt; { @@ -58,6 +58,7 @@ public interface TreeMapper extends BaseMapperX&lt;TreeDO&gt; {
58 .betweenIfPresent(TreeDO::getUpdatetime, reqVO.getUpdatetime()) 58 .betweenIfPresent(TreeDO::getUpdatetime, reqVO.getUpdatetime())
59 .eqIfPresent(TreeDO::getRemark, reqVO.getRemark()) 59 .eqIfPresent(TreeDO::getRemark, reqVO.getRemark())
60 .eqIfPresent(TreeDO::getDeptId, reqVO.getDeptId()) 60 .eqIfPresent(TreeDO::getDeptId, reqVO.getDeptId())
  61 + .orderByDesc(TreeDO::getUpdatetime)
61 .orderByDesc(TreeDO::getId); 62 .orderByDesc(TreeDO::getId);
62 63
63 return selectJoinPage(reqVO, TreeDO.class, wrapper); 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&lt;TreeChangeLogDO&gt; { @@ -22,7 +22,7 @@ public interface TreeChangeLogMapper extends BaseMapperX&lt;TreeChangeLogDO&gt; {
22 default PageResult<TreeChangeLogDO> selectPage(TreeChangeLogPageReqVO reqVO) { 22 default PageResult<TreeChangeLogDO> selectPage(TreeChangeLogPageReqVO reqVO) {
23 23
24 MPJLambdaWrapperX<TreeChangeLogDO> wrapper = new MPJLambdaWrapperX<TreeChangeLogDO>() 24 MPJLambdaWrapperX<TreeChangeLogDO> wrapper = new MPJLambdaWrapperX<TreeChangeLogDO>()
25 - .selectAs(DeptDO::getName, TreeChangeLogDO::getMaintainunit) 25 + .selectAs(DeptDO::getName, TreeChangeLogDO::getMaintainunitName)
26 .selectAll(TreeChangeLogDO.class) 26 .selectAll(TreeChangeLogDO.class)
27 .leftJoin(DeptDO.class, DeptDO::getId, TreeChangeLogDO::getMaintainunit) 27 .leftJoin(DeptDO.class, DeptDO::getId, TreeChangeLogDO::getMaintainunit)
28 .eqIfPresent(TreeChangeLogDO::getTreeid, reqVO.getTreeid()) 28 .eqIfPresent(TreeChangeLogDO::getTreeid, reqVO.getTreeid())
@@ -59,6 +59,7 @@ public interface TreeChangeLogMapper extends BaseMapperX&lt;TreeChangeLogDO&gt; { @@ -59,6 +59,7 @@ public interface TreeChangeLogMapper extends BaseMapperX&lt;TreeChangeLogDO&gt; {
59 .eqIfPresent(TreeChangeLogDO::getUpdateby, reqVO.getUpdateby()) 59 .eqIfPresent(TreeChangeLogDO::getUpdateby, reqVO.getUpdateby())
60 .betweenIfPresent(TreeChangeLogDO::getUpdatetime, reqVO.getUpdatetime()) 60 .betweenIfPresent(TreeChangeLogDO::getUpdatetime, reqVO.getUpdatetime())
61 .eqIfPresent(TreeChangeLogDO::getRemark, reqVO.getRemark()) 61 .eqIfPresent(TreeChangeLogDO::getRemark, reqVO.getRemark())
  62 + .orderByDesc(TreeChangeLogDO::getUpdatetime)
62 .orderByDesc(TreeChangeLogDO::getId); 63 .orderByDesc(TreeChangeLogDO::getId);
63 64
64 return selectJoinPage(reqVO, TreeChangeLogDO.class, wrapper); 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,4 +168,11 @@ public interface ErrorCodeConstants {
168 // ========== 站内信发送 1-002-028-000 ========== 168 // ========== 站内信发送 1-002-028-000 ==========
169 ErrorCode NOTIFY_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_028_000, "模板参数({})缺失"); 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 \ No newline at end of file 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 \ No newline at end of file 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 +}