Browse Source

产品配置开发

zhangqiankun 3 years ago
parent
commit
79e8a17561
80 changed files with 3465 additions and 181 deletions
  1. 46 1
      saas-account/src/main/java/com/persagy/account/controller/SaasAccountController.java
  2. 1 1
      saas-account/src/main/java/com/persagy/account/controller/SaasAccountProjectController.java
  3. 2 2
      saas-account/src/main/java/com/persagy/account/controller/SaasAreaController.java
  4. 1 1
      saas-account/src/main/java/com/persagy/account/controller/SaasAuthCenterController.java
  5. 7 12
      saas-account/src/main/java/com/persagy/account/controller/SaasCommonController.java
  6. 84 19
      saas-account/src/main/java/com/persagy/account/controller/SaasFunctionController.java
  7. 2 2
      saas-account/src/main/java/com/persagy/account/controller/SaasGroupController.java
  8. 146 0
      saas-account/src/main/java/com/persagy/account/controller/SaasGroupProductController.java
  9. 70 7
      saas-account/src/main/java/com/persagy/account/controller/SaasMenuController.java
  10. 9 2
      saas-account/src/main/java/com/persagy/account/controller/SaasProductController.java
  11. 130 0
      saas-account/src/main/java/com/persagy/account/controller/SaasProductPageController.java
  12. 1 1
      saas-account/src/main/java/com/persagy/account/controller/SaasProjectController.java
  13. 113 0
      saas-account/src/main/java/com/persagy/account/controller/SaasProjectProductController.java
  14. 17 65
      saas-account/src/main/java/com/persagy/account/manage/SaasAuthHandler.java
  15. 1 1
      saas-account/src/main/java/com/persagy/account/manage/SaasGroupProjectHandler.java
  16. 251 0
      saas-account/src/main/java/com/persagy/account/manage/SaasProductHandler.java
  17. 36 15
      saas-account/src/main/java/com/persagy/account/manage/SaasRoleMenuHandler.java
  18. 15 3
      saas-account/src/main/java/com/persagy/account/mapper/SaasAccountMapper.java
  19. 12 1
      saas-account/src/main/java/com/persagy/account/mapper/SaasFunctionMapper.java
  20. 30 0
      saas-account/src/main/java/com/persagy/account/mapper/SaasGroupProductDetailMapper.java
  21. 19 0
      saas-account/src/main/java/com/persagy/account/mapper/SaasGroupProductMapper.java
  22. 9 0
      saas-account/src/main/java/com/persagy/account/mapper/SaasMenuMapper.java
  23. 54 0
      saas-account/src/main/java/com/persagy/account/mapper/SaasProductPageMapper.java
  24. 47 0
      saas-account/src/main/java/com/persagy/account/mapper/SaasProjectProductMapper.java
  25. 14 0
      saas-account/src/main/java/com/persagy/account/pojo/dto/SaasFunction.java
  26. 233 0
      saas-account/src/main/java/com/persagy/account/pojo/dto/SaasGroupProduct.java
  27. 85 0
      saas-account/src/main/java/com/persagy/account/pojo/dto/SaasGroupProductDetail.java
  28. 22 5
      saas-account/src/main/java/com/persagy/account/pojo/dto/SaasMenu.java
  29. 21 0
      saas-account/src/main/java/com/persagy/account/pojo/dto/SaasProduct.java
  30. 291 0
      saas-account/src/main/java/com/persagy/account/pojo/dto/SaasProductPage.java
  31. 159 0
      saas-account/src/main/java/com/persagy/account/pojo/dto/SaasProjectProduct.java
  32. 14 0
      saas-account/src/main/java/com/persagy/account/pojo/dto/SaasRoleMenu.java
  33. 3 0
      saas-account/src/main/java/com/persagy/account/pojo/vo/account/SaasAccountQueryVO.java
  34. 6 3
      saas-account/src/main/java/com/persagy/account/pojo/vo/function/SaasFunctionCreateVO.java
  35. 0 3
      saas-account/src/main/java/com/persagy/account/pojo/vo/function/SaasFunctionDeleteVO.java
  36. 4 1
      saas-account/src/main/java/com/persagy/account/pojo/vo/function/SaasFunctionQueryVO.java
  37. 47 0
      saas-account/src/main/java/com/persagy/account/pojo/vo/function/SaasMenuFunctionVO.java
  38. 6 3
      saas-account/src/main/java/com/persagy/account/pojo/vo/menu/SaasMenuCreateVO.java
  39. 8 2
      saas-account/src/main/java/com/persagy/account/pojo/vo/menu/SaasMenuQueryVO.java
  40. 32 0
      saas-account/src/main/java/com/persagy/account/pojo/vo/menu/SaasMenuSort.java
  41. 32 0
      saas-account/src/main/java/com/persagy/account/pojo/vo/menu/SaasMenuSortVO.java
  42. 63 0
      saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasGroupProductCreateVO.java
  43. 31 0
      saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasGroupProductDeleteVO.java
  44. 36 0
      saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasGroupProductPageVO.java
  45. 34 0
      saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasGroupProductQueryVO.java
  46. 62 0
      saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasProductPageCreateVO.java
  47. 30 0
      saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasProductPageDeleteVO.java
  48. 36 0
      saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasProductPageTreeVO.java
  49. 41 0
      saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasProjectProductBatchVO.java
  50. 37 0
      saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasProjectProductCreateVO.java
  51. 36 0
      saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasProjectProductQueryVO.java
  52. 9 0
      saas-account/src/main/java/com/persagy/account/service/ISaasAccountService.java
  53. 22 1
      saas-account/src/main/java/com/persagy/account/service/ISaasFunctionService.java
  54. 17 0
      saas-account/src/main/java/com/persagy/account/service/ISaasGroupProductDetailService.java
  55. 46 0
      saas-account/src/main/java/com/persagy/account/service/ISaasGroupProductService.java
  56. 26 0
      saas-account/src/main/java/com/persagy/account/service/ISaasMenuService.java
  57. 51 0
      saas-account/src/main/java/com/persagy/account/service/ISaasProductPageService.java
  58. 42 0
      saas-account/src/main/java/com/persagy/account/service/ISaasProjectProductService.java
  59. 22 1
      saas-account/src/main/java/com/persagy/account/service/impl/SaasAccountServiceImpl.java
  60. 28 2
      saas-account/src/main/java/com/persagy/account/service/impl/SaasFunctionServiceImpl.java
  61. 21 0
      saas-account/src/main/java/com/persagy/account/service/impl/SaasGroupProductDetailServiceImpl.java
  62. 71 0
      saas-account/src/main/java/com/persagy/account/service/impl/SaasGroupProductServiceImpl.java
  63. 44 1
      saas-account/src/main/java/com/persagy/account/service/impl/SaasMenuServiceImpl.java
  64. 38 0
      saas-account/src/main/java/com/persagy/account/service/impl/SaasProductPageServiceImpl.java
  65. 43 0
      saas-account/src/main/java/com/persagy/account/service/impl/SaasProjectProductServiceImpl.java
  66. 29 2
      saas-account/src/main/resources/mapper/SaasAccountMapper.xml
  67. 21 1
      saas-account/src/main/resources/mapper/SaasFunctionMapper.xml
  68. 18 0
      saas-account/src/main/resources/mapper/SaasGroupProductDetailMapper.xml
  69. 20 0
      saas-account/src/main/resources/mapper/SaasGroupProductMapper.xml
  70. 13 4
      saas-account/src/main/resources/mapper/SaasMenuMapper.xml
  71. 1 0
      saas-account/src/main/resources/mapper/SaasProductMapper.xml
  72. 80 0
      saas-account/src/main/resources/mapper/SaasProductPageMapper.xml
  73. 42 0
      saas-account/src/main/resources/mapper/SaasProjectProductMapper.xml
  74. 1 0
      saas-account/src/main/resources/mapper/SaasRoleMenuMapper.xml
  75. 7 0
      saas-common/src/main/java/com/persagy/common/constant/SaasCommonConstant.java
  76. 27 0
      saas-doc/doc/saas-platform_dict.txt
  77. 239 18
      saas-doc/doc/saas_platform.md
  78. BIN
      saas-doc/doc/运维平台单点登录授权文档.docx
  79. BIN
      saas-doc/doc/运维管理3.0-数据库表设计.xlsx
  80. 1 1
      saas-gateway/src/main/resources/application.yml

+ 46 - 1
saas-account/src/main/java/com/persagy/account/controller/SaasAccountController.java

@@ -2,9 +2,9 @@ package com.persagy.account.controller;
 
 
 import java.util.List;
 import java.util.List;
 
 
-import com.persagy.account.pojo.vo.account.*;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -20,6 +20,13 @@ import com.persagy.account.config.ApplicationProperties;
 import com.persagy.account.manage.SaasAccountHandler;
 import com.persagy.account.manage.SaasAccountHandler;
 import com.persagy.account.pojo.dto.SaasAccount;
 import com.persagy.account.pojo.dto.SaasAccount;
 import com.persagy.account.pojo.dto.SaasRole;
 import com.persagy.account.pojo.dto.SaasRole;
+import com.persagy.account.pojo.vo.account.SaasAccountCreateVO;
+import com.persagy.account.pojo.vo.account.SaasAccountDeleteVO;
+import com.persagy.account.pojo.vo.account.SaasAccountGroupVO;
+import com.persagy.account.pojo.vo.account.SaasAccountPageVO;
+import com.persagy.account.pojo.vo.account.SaasAccountQueryByNameVO;
+import com.persagy.account.pojo.vo.account.SaasAccountQueryVO;
+import com.persagy.account.pojo.vo.thirty.SaasCodeVO;
 import com.persagy.account.service.ISaasAccountService;
 import com.persagy.account.service.ISaasAccountService;
 import com.persagy.account.service.ISaasRoleService;
 import com.persagy.account.service.ISaasRoleService;
 import com.persagy.common.constant.SaasCommonConstant;
 import com.persagy.common.constant.SaasCommonConstant;
@@ -63,6 +70,9 @@ public class SaasAccountController {
 	
 	
     @Autowired
     @Autowired
     private ISaasAccountService saasAccountService;
     private ISaasAccountService saasAccountService;
+    
+	@Autowired
+	private RedisTemplate<String, Object> redisTemplate;
 
 
     /**
     /**
      * 账号信息查询
      * 账号信息查询
@@ -145,6 +155,24 @@ public class SaasAccountController {
     }
     }
     
     
     /**
     /**
+     * 根据角色ID,查询出此角色下所有的账号信息
+     */
+    @ApiOperation(value = "查询角色账号信息")
+    @RequestMapping(value = "queryRoleAccountPage")
+    public ResponseResult queryRoleAccountPage(@RequestBody @Validated SaasAccountPageVO queryVO) {
+    	if (StringUtil.isBlank(queryVO.getRoleId()) && CollectionUtil.isEmpty(queryVO.getRoleIds())) {
+    		return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "角色ID,不可为空");
+		}
+    	if (CollectionUtil.isNotEmpty(queryVO.getRoleIds()) && queryVO.getRoleIds().size() > 1000) {
+    		return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "角色ID数量过多,请分批查询");
+		}
+    	SaasAccount saasAccount = new SaasAccount();
+    	BeanUtils.copyProperties(queryVO, saasAccount);
+    	Page<SaasAccount> accounts = this.saasAccountService.queryRoleAccountPage(saasAccount, queryVO.getRoleIds(), queryVO.getPage(), queryVO.getSize());
+    	return ResponseResultUtil.successResult(accounts.getRecords(), accounts.getTotal());
+    }
+    
+    /**
      * 根据账号名称获取账号信息
      * 根据账号名称获取账号信息
      */
      */
     @ApiOperation(value = "根据账号名称获取账号信息")
     @ApiOperation(value = "根据账号名称获取账号信息")
@@ -161,6 +189,23 @@ public class SaasAccountController {
     }
     }
     
     
     /**
     /**
+     * 获取账号信息
+     */
+    @ApiOperation(value = "获取账号信息")
+    @RequestMapping(value = "getSaasAccount")
+    public ResponseResult getSaasAccount(@RequestBody @Validated SaasCodeVO model) {
+    	String accountId = (String) this.redisTemplate.opsForHash().get(model.getClientId(), SaasCommonConstant.ACCOUNT_ID_REDIS_HASH_KEY);
+    	if (StringUtil.isBlank(accountId)) {
+    		return ResponseResultUtil.errorResult(ResponseCode.A0201.getCode(), ResponseCode.A0201.getDesc());
+		}
+    	SaasAccount saasAccount = this.saasAccountService.getOne(accountId, null, null);
+    	if (saasAccount == null) {
+    		return ResponseResultUtil.errorResult(ResponseCode.A0201.getCode(), ResponseCode.A0201.getDesc());
+		}
+    	return ResponseResultUtil.successResult(saasAccount);
+    }
+    
+    /**
      * 新增,登录用户名,全局唯一
      * 新增,登录用户名,全局唯一
      */
      */
 	@ApiOperation(value = "保存")
 	@ApiOperation(value = "保存")

+ 1 - 1
saas-account/src/main/java/com/persagy/account/controller/SaasAccountProjectController.java

@@ -94,7 +94,7 @@ public class SaasAccountProjectController {
     	saasAccount.setValid(SaasCommonConstant.STATUS_1);
     	saasAccount.setValid(SaasCommonConstant.STATUS_1);
     	
     	
     	// 先判断此账号的权限
     	// 先判断此账号的权限
-    	Map<String, String> authMap = this.saasAuthHandler.validGroupProjectAuth(pageVO.getAccountId(), pageVO.getGroupCode());
+    	Map<String, String> authMap = this.saasAuthHandler.validAccountAuth(pageVO.getAccountId(), pageVO.getGroupCode());
     	if (StringUtil.isNotBlank(authMap.get(SaasCommonConstant.GROUP_CODE))) {
     	if (StringUtil.isNotBlank(authMap.get(SaasCommonConstant.GROUP_CODE))) {
     		pageVO.setGroupCode(authMap.get(SaasCommonConstant.GROUP_CODE));
     		pageVO.setGroupCode(authMap.get(SaasCommonConstant.GROUP_CODE));
 		}
 		}

+ 2 - 2
saas-account/src/main/java/com/persagy/account/controller/SaasAreaController.java

@@ -95,7 +95,7 @@ public class SaasAreaController {
     	// 如果需要统计项目
     	// 如果需要统计项目
     	if (queryVO.isTotal()) {
     	if (queryVO.isTotal()) {
     		// 账号数据可见性的权限鉴定
     		// 账号数据可见性的权限鉴定
-    		Map<String, String> authMap = this.saasAuthHandler.validGroupProjectAuth(queryVO.getAccountId(), queryVO.getGroupCode());
+    		Map<String, String> authMap = this.saasAuthHandler.validAccountAuth(queryVO.getAccountId(), queryVO.getGroupCode());
     		if (StringUtil.isNotBlank(authMap.get(SaasCommonConstant.GROUP_CODE))) {
     		if (StringUtil.isNotBlank(authMap.get(SaasCommonConstant.GROUP_CODE))) {
     			queryVO.setGroupCode(authMap.get(SaasCommonConstant.GROUP_CODE));
     			queryVO.setGroupCode(authMap.get(SaasCommonConstant.GROUP_CODE));
     		}
     		}
@@ -118,7 +118,7 @@ public class SaasAreaController {
     public ResponseResult queryAllowSaasAreaList(@RequestBody @Validated SaasAreaQueryVO queryVO) {
     public ResponseResult queryAllowSaasAreaList(@RequestBody @Validated SaasAreaQueryVO queryVO) {
     	boolean isAdmin = false;
     	boolean isAdmin = false;
     	// 账号数据可见性的权限鉴定
     	// 账号数据可见性的权限鉴定
-		Map<String, String> authMap = this.saasAuthHandler.validGroupProjectAuth(queryVO.getAccountId(), queryVO.getGroupCode());
+		Map<String, String> authMap = this.saasAuthHandler.validAccountAuth(queryVO.getAccountId(), queryVO.getGroupCode());
 		if (StringUtil.isNotBlank(authMap.get(SaasCommonConstant.GROUP_CODE))) {
 		if (StringUtil.isNotBlank(authMap.get(SaasCommonConstant.GROUP_CODE))) {
 			queryVO.setGroupCode(authMap.get(SaasCommonConstant.GROUP_CODE));
 			queryVO.setGroupCode(authMap.get(SaasCommonConstant.GROUP_CODE));
 		}
 		}

+ 1 - 1
saas-account/src/main/java/com/persagy/account/controller/SaasAuthCenterController.java

@@ -98,7 +98,7 @@ public class SaasAuthCenterController {
     	long expire = Long.parseLong(this.properties.getTokenExpire());
     	long expire = Long.parseLong(this.properties.getTokenExpire());
     	String accountId = (String) this.redisTemplate.opsForHash().get(model.getClientId(), SaasCommonConstant.ACCOUNT_ID_REDIS_HASH_KEY);
     	String accountId = (String) this.redisTemplate.opsForHash().get(model.getClientId(), SaasCommonConstant.ACCOUNT_ID_REDIS_HASH_KEY);
     	if (StringUtil.isBlank(accountId)) {
     	if (StringUtil.isBlank(accountId)) {
-    		return ResponseResultUtil.errorResult(ResponseCode.A0402.getCode(), ResponseCode.A0402.getDesc());
+    		return ResponseResultUtil.errorResult(ResponseCode.A0201.getCode(), ResponseCode.A0201.getDesc());
 		}
 		}
     	// token放入redis,key-accountId
     	// token放入redis,key-accountId
     	String token = (String) this.redisTemplate.opsForValue().get(accountId);
     	String token = (String) this.redisTemplate.opsForValue().get(accountId);

+ 7 - 12
saas-account/src/main/java/com/persagy/account/controller/SaasCommonController.java

@@ -8,11 +8,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.bind.annotation.RestController;
 
 
+import com.persagy.account.pojo.vo.BaseGroupVO;
+import com.persagy.common.constant.SaasCommonConstant;
 import com.persagy.common.utils.ResponseResult;
 import com.persagy.common.utils.ResponseResult;
 import com.persagy.common.utils.ResponseResultUtil;
 import com.persagy.common.utils.ResponseResultUtil;
-import com.persagy.account.pojo.vo.BaseGroupVO;
 
 
-import cn.hutool.core.util.BooleanUtil;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiOperation;
 
 
@@ -37,16 +37,11 @@ public class SaasCommonController {
      * 查询菜单历史记录
      * 查询菜单历史记录
      */
      */
     @ApiOperation(value = "查询菜单历史记录")
     @ApiOperation(value = "查询菜单历史记录")
-    @RequestMapping(value = "queryMenuHistory")
-    public ResponseResult queryMenuHistory(@RequestBody @Validated BaseGroupVO model) {
-    	Boolean result = null;
-    	String accountId = model.getAccountId() + "_history_menu";
-    	Long size = redisTemplate.opsForZSet().size(accountId);
-    	if (size < 10) {
-    		result = redisTemplate.opsForZSet().add(accountId, model, System.currentTimeMillis());
-		} else {
-		}
-    	return BooleanUtil.isTrue(result) ? ResponseResultUtil.successResult("添加成功") : ResponseResultUtil.errorResult("添加失败");
+    @RequestMapping(value = "test")
+    public ResponseResult test(@RequestBody @Validated BaseGroupVO model) {
+    	this.redisTemplate.opsForSet().add(SaasCommonConstant.SAAS_CLIENT_ID_REDIS_KEY, "EF4C04481E604506995AF5CF5BF2E395");
+    	this.redisTemplate.opsForHash().put("EF4C04481E604506995AF5CF5BF2E395", SaasCommonConstant.INNER_CLIENT_REDIS_HASH_KEY, true);
+    	return ResponseResultUtil.successResult();
     }
     }
     
     
 }
 }

+ 84 - 19
saas-account/src/main/java/com/persagy/account/controller/SaasFunctionController.java

@@ -13,20 +13,24 @@ import org.springframework.web.bind.annotation.RestController;
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Lists;
-import com.persagy.common.enums.ResponseCode;
-import com.persagy.common.utils.ResponseResult;
-import com.persagy.common.utils.ResponseResultUtil;
-import com.persagy.common.utils.StringUtil;
+import com.persagy.account.manage.SaasProductHandler;
 import com.persagy.account.manage.SaasRoleMenuHandler;
 import com.persagy.account.manage.SaasRoleMenuHandler;
 import com.persagy.account.pojo.dto.SaasFunction;
 import com.persagy.account.pojo.dto.SaasFunction;
 import com.persagy.account.pojo.dto.SaasMenu;
 import com.persagy.account.pojo.dto.SaasMenu;
+import com.persagy.account.pojo.dto.SaasProductPage;
 import com.persagy.account.pojo.dto.SaasRole;
 import com.persagy.account.pojo.dto.SaasRole;
 import com.persagy.account.pojo.vo.function.SaasFunctionCreateVO;
 import com.persagy.account.pojo.vo.function.SaasFunctionCreateVO;
 import com.persagy.account.pojo.vo.function.SaasFunctionDeleteVO;
 import com.persagy.account.pojo.vo.function.SaasFunctionDeleteVO;
 import com.persagy.account.pojo.vo.function.SaasFunctionQueryVO;
 import com.persagy.account.pojo.vo.function.SaasFunctionQueryVO;
+import com.persagy.account.pojo.vo.function.SaasMenuFunctionVO;
 import com.persagy.account.service.ISaasFunctionService;
 import com.persagy.account.service.ISaasFunctionService;
 import com.persagy.account.service.ISaasMenuService;
 import com.persagy.account.service.ISaasMenuService;
+import com.persagy.account.service.ISaasProductPageService;
 import com.persagy.account.service.ISaasRoleService;
 import com.persagy.account.service.ISaasRoleService;
+import com.persagy.common.enums.ResponseCode;
+import com.persagy.common.utils.ResponseResult;
+import com.persagy.common.utils.ResponseResultUtil;
+import com.persagy.common.utils.StringUtil;
 
 
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.Api;
@@ -52,12 +56,38 @@ public class SaasFunctionController {
     @Autowired
     @Autowired
     private ISaasRoleService saasRoleService;
     private ISaasRoleService saasRoleService;
     
     
-    @Autowired
+	@Autowired
+	private SaasProductHandler saasProductHandler;
+
+	@Autowired
     private ISaasFunctionService saasFunctionService;
     private ISaasFunctionService saasFunctionService;
     
     
     @Autowired
     @Autowired
     private SaasRoleMenuHandler saasRoleMenuHandler;
     private SaasRoleMenuHandler saasRoleMenuHandler;
     
     
+	@Autowired
+	private ISaasProductPageService saasProductPageService;
+	
+    /**
+     * 账号、菜单所拥有的功能点查询,权限过滤
+     * 此方法不考虑菜单与产品同时存在的情况,后续所有的权限都由产品控制
+     */
+    @ApiOperation(value = "菜单下功能点查询")
+    @RequestMapping(value = "queryMenuFunctionList")
+    public ResponseResult queryMenuFunctionList(@RequestBody @Validated SaasMenuFunctionVO model) {
+    	// 优先判断此账号是否拥有此菜单的权限,后续判断
+    	// 判断此菜单ID是否为产品,后续的落地菜单,均挂产品,此项判断仅为兼容之前的数据
+    	if (StringUtil.isBlank(model.getProductId())) {
+			// 产品ID不存在,直接获取此账号所拥有的功能点信息集合,返回
+    		List<SaasFunction> functions = this.saasFunctionService.queryAccountFunctionList(model.getAccountId(), model.getGroupCode(), model.getMenuId());
+    		return ResponseResultUtil.successResult(functions);
+    	}
+    	// 根据账号、集团、项目、视角获取满足条件的产品页面集合
+    	List<SaasProductPage> productPages = this.saasProductHandler.queryProductPageList(model.getAccountId(), model.getProductId(), 
+    			model.getAngle(), model.getGroupCode(), model.getProjectId());
+        return ResponseResultUtil.successResult(productPages);
+    }
+	
     /**
     /**
      * 功能点列表查询
      * 功能点列表查询
      */
      */
@@ -66,7 +96,7 @@ public class SaasFunctionController {
     public ResponseResult querySaasFunctionList(@RequestBody @Validated SaasFunctionQueryVO queryVO) {
     public ResponseResult querySaasFunctionList(@RequestBody @Validated SaasFunctionQueryVO queryVO) {
     	LambdaQueryWrapper<SaasFunction> queryWrapper = new SaasFunction.Builder().createQueryWrapper()
     	LambdaQueryWrapper<SaasFunction> queryWrapper = new SaasFunction.Builder().createQueryWrapper()
     			.menuIdEq(queryVO.getMenuId()).funCodeEq(queryVO.getFunCode()).updateUserEq(queryVO.getUpdateUser())
     			.menuIdEq(queryVO.getMenuId()).funCodeEq(queryVO.getFunCode()).updateUserEq(queryVO.getUpdateUser())
-    			.funNameLike(queryVO.getFunName()).builderQueryWrapper();
+    			.pageIdEq(queryVO.getPageId()).funNameLike(queryVO.getFunName()).builderQueryWrapper();
     	queryWrapper.orderByAsc(SaasFunction::getFunSort);
     	queryWrapper.orderByAsc(SaasFunction::getFunSort);
     	List<SaasFunction> list = this.saasFunctionService.list(queryWrapper);
     	List<SaasFunction> list = this.saasFunctionService.list(queryWrapper);
     	if (CollectionUtil.isEmpty(list)) {
     	if (CollectionUtil.isEmpty(list)) {
@@ -82,7 +112,7 @@ public class SaasFunctionController {
     @RequestMapping(value = "querySaasRoleFunctions")
     @RequestMapping(value = "querySaasRoleFunctions")
     public ResponseResult querySaasRoleFunctions(@RequestBody @Validated SaasFunctionQueryVO queryVO) {
     public ResponseResult querySaasRoleFunctions(@RequestBody @Validated SaasFunctionQueryVO queryVO) {
     	if (StringUtil.isBlank(queryVO.getRoleId())) {
     	if (StringUtil.isBlank(queryVO.getRoleId())) {
-    		return ResponseResultUtil.errorResult(ResponseCode.A0402.getCode(), "ID不可为空");
+    		return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "ID不可为空");
 		}
 		}
     	// 验证角色是否存在
     	// 验证角色是否存在
     	SaasRole saasRole = this.saasRoleService.getOne(queryVO.getRoleId());
     	SaasRole saasRole = this.saasRoleService.getOne(queryVO.getRoleId());
@@ -90,7 +120,7 @@ public class SaasFunctionController {
     		return ResponseResultUtil.errorResult(ResponseCode.C0320.getCode(), "数据不存在");
     		return ResponseResultUtil.errorResult(ResponseCode.C0320.getCode(), "数据不存在");
 		}
 		}
     	// 优先查询出存在功能点的信息集合
     	// 优先查询出存在功能点的信息集合
-    	List<SaasFunction> functions = this.saasFunctionService.queryRoleFunctionList(queryVO.getGroupCode(), queryVO.getRoleId(), null);
+    	List<SaasFunction> functions = this.saasFunctionService.queryRoleFunctionList(queryVO.getGroupCode(), queryVO.getRoleId(), null, null);
     	// 再查询出此角色下,所有不存在功能点的菜单集合
     	// 再查询出此角色下,所有不存在功能点的菜单集合
     	List<SaasMenu> menus = this.saasMenuService.queryMenuAndEmptyFunction(queryVO.getGroupCode(), queryVO.getRoleId());
     	List<SaasMenu> menus = this.saasMenuService.queryMenuAndEmptyFunction(queryVO.getGroupCode(), queryVO.getRoleId());
     	
     	
@@ -106,10 +136,9 @@ public class SaasFunctionController {
 	@ApiOperation(value = "保存")
 	@ApiOperation(value = "保存")
 	@RequestMapping(value = "createSaasFunction")
 	@RequestMapping(value = "createSaasFunction")
     public ResponseResult createSaasFunction(@RequestBody @Validated SaasFunctionCreateVO createVO) {
     public ResponseResult createSaasFunction(@RequestBody @Validated SaasFunctionCreateVO createVO) {
-		// 验证菜单ID是否存在
-		SaasMenu menu = this.saasMenuService.getById(createVO.getMenuId());
-		if (menu == null) {
-			return ResponseResultUtil.errorResult(ResponseCode.C0320.getCode(), "不存在此菜单");
+		ResponseResult validRequestParam = this.validRequestParam(createVO.getMenuId(), createVO.getPageId(), createVO.getFunCode(), null);
+		if (validRequestParam != null) {
+			return validRequestParam;
 		}
 		}
 		SaasFunction saasFunction = new SaasFunction();
 		SaasFunction saasFunction = new SaasFunction();
 		BeanUtils.copyProperties(createVO, saasFunction);
 		BeanUtils.copyProperties(createVO, saasFunction);
@@ -128,10 +157,9 @@ public class SaasFunctionController {
 		if (StringUtil.isBlank(createVO.getId())) {
 		if (StringUtil.isBlank(createVO.getId())) {
 			return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "功能ID不可为空");
 			return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "功能ID不可为空");
 		}
 		}
-		// 验证菜单ID是否存在
-		SaasMenu menu = this.saasMenuService.getById(createVO.getMenuId());
-		if (menu == null) {
-			return ResponseResultUtil.errorResult(ResponseCode.C0320.getCode(), "不存在此菜单");
+		ResponseResult validRequestParam = this.validRequestParam(createVO.getMenuId(), createVO.getPageId(), createVO.getFunCode(), createVO.getId());
+		if (validRequestParam != null) {
+			return validRequestParam;
 		}
 		}
 		SaasFunction saasFunction = new SaasFunction();
 		SaasFunction saasFunction = new SaasFunction();
 		BeanUtils.copyProperties(createVO, saasFunction);
 		BeanUtils.copyProperties(createVO, saasFunction);
@@ -146,11 +174,48 @@ public class SaasFunctionController {
     @ApiOperation(value = "删除")
     @ApiOperation(value = "删除")
     @RequestMapping(value = "deleteSaasFunction")
     @RequestMapping(value = "deleteSaasFunction")
     public ResponseResult deleteSaasFunction(@RequestBody @Validated SaasFunctionDeleteVO deleteVO) {
     public ResponseResult deleteSaasFunction(@RequestBody @Validated SaasFunctionDeleteVO deleteVO) {
-    	if (StringUtil.isAllBlank(deleteVO.getId(), deleteVO.getMenuId())) {
-    		return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "功能ID与菜单ID至少存在其一");
+    	if (StringUtil.isBlank(deleteVO.getId())) {
+    		return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "功能ID,不可为空");
 		}
 		}
-        boolean result = this.saasRoleMenuHandler.deleteSaasFunction(deleteVO.getId(), deleteVO.getMenuId());
+        boolean result = this.saasRoleMenuHandler.deleteSaasFunction(deleteVO.getId());
         return result ? ResponseResultUtil.successResult("删除成功") : ResponseResultUtil.errorResult("删除失败");
         return result ? ResponseResultUtil.successResult("删除成功") : ResponseResultUtil.errorResult("删除失败");
     }
     }
     
     
+    /**
+     * 
+     * @return
+     */
+    private ResponseResult validRequestParam(String menuId, String pageId, String funCode, String funId) {
+    	if (StringUtil.isAllBlank(menuId, pageId)) {
+			return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "菜单ID与页面ID必填其一");
+		}
+		if (StringUtil.isNotBlank(menuId)) {
+			SaasMenu menu = this.saasMenuService.getById(menuId);
+			if (menu == null) {
+				return ResponseResultUtil.errorResult(ResponseCode.C0320.getCode(), "不存在此菜单");
+			}
+			// 验证功能编码的唯一性
+			if (StringUtil.isNotBlank(funCode)) {
+				boolean validFunCode = this.saasFunctionService.validFunCode(menuId, null, funCode, funId);
+				if (!validFunCode) {
+					return ResponseResultUtil.errorResult(ResponseCode.A0402.getCode(), "此编码已存在");
+				}
+			}
+		}
+		if (StringUtil.isNotBlank(pageId)) {
+			SaasProductPage page = this.saasProductPageService.getById(pageId);
+			if (page == null) {
+				return ResponseResultUtil.errorResult(ResponseCode.C0320.getCode(), "不存在此页面");
+			}
+			// 验证功能编码的唯一性
+			if (StringUtil.isNotBlank(funCode)) {
+				boolean validFunCode = this.saasFunctionService.validFunCode(null, pageId, funCode, funId);
+				if (!validFunCode) {
+					return ResponseResultUtil.errorResult(ResponseCode.A0402.getCode(), "此编码已存在");
+				}
+			}
+		}
+    	return null;
+    }
+    
 }
 }

+ 2 - 2
saas-account/src/main/java/com/persagy/account/controller/SaasGroupController.java

@@ -62,7 +62,7 @@ public class SaasGroupController {
     @RequestMapping(value = "querySaasGroupList")
     @RequestMapping(value = "querySaasGroupList")
     public ResponseResult querySaasGroupList(@RequestBody @Validated SaasGroupQueryVO queryVO) {
     public ResponseResult querySaasGroupList(@RequestBody @Validated SaasGroupQueryVO queryVO) {
     	// 账号数据可见性的权限鉴定
     	// 账号数据可见性的权限鉴定
-		Map<String, String> authMap = this.saasAuthHandler.validGroupProjectAuth(queryVO.getAccountId(), queryVO.getGroupCode());
+		Map<String, String> authMap = this.saasAuthHandler.validAccountAuth(queryVO.getAccountId(), queryVO.getGroupCode());
 		if (StringUtil.isNotBlank(authMap.get(SaasCommonConstant.GROUP_CODE))) {
 		if (StringUtil.isNotBlank(authMap.get(SaasCommonConstant.GROUP_CODE))) {
 			queryVO.setGroupCode(authMap.get(SaasCommonConstant.GROUP_CODE));
 			queryVO.setGroupCode(authMap.get(SaasCommonConstant.GROUP_CODE));
 		}
 		}
@@ -89,7 +89,7 @@ public class SaasGroupController {
     @RequestMapping(value = "queryGroupProjectTree")
     @RequestMapping(value = "queryGroupProjectTree")
     public ResponseResult queryGroupProjectTree(@RequestBody @Validated SaasGroupQueryVO queryVO) {
     public ResponseResult queryGroupProjectTree(@RequestBody @Validated SaasGroupQueryVO queryVO) {
     	// 账号数据可见性的权限鉴定
     	// 账号数据可见性的权限鉴定
-		Map<String, String> authMap = this.saasAuthHandler.validGroupProjectAuth(queryVO.getAccountId(), queryVO.getGroupCode());
+		Map<String, String> authMap = this.saasAuthHandler.validAccountAuth(queryVO.getAccountId(), queryVO.getGroupCode());
 		if (StringUtil.isNotBlank(authMap.get(SaasCommonConstant.GROUP_CODE))) {
 		if (StringUtil.isNotBlank(authMap.get(SaasCommonConstant.GROUP_CODE))) {
 			queryVO.setGroupCode(authMap.get(SaasCommonConstant.GROUP_CODE));
 			queryVO.setGroupCode(authMap.get(SaasCommonConstant.GROUP_CODE));
 		}
 		}

+ 146 - 0
saas-account/src/main/java/com/persagy/account/controller/SaasGroupProductController.java

@@ -0,0 +1,146 @@
+package com.persagy.account.controller;
+
+import java.util.List;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.google.common.collect.Lists;
+import com.persagy.account.pojo.dto.SaasGroupProduct;
+import com.persagy.account.pojo.dto.SaasProjectProduct;
+import com.persagy.account.pojo.vo.product.SaasGroupProductCreateVO;
+import com.persagy.account.pojo.vo.product.SaasGroupProductDeleteVO;
+import com.persagy.account.pojo.vo.product.SaasGroupProductPageVO;
+import com.persagy.account.pojo.vo.product.SaasGroupProductQueryVO;
+import com.persagy.account.service.ISaasGroupProductService;
+import com.persagy.account.service.ISaasProjectProductService;
+import com.persagy.common.constant.SaasCommonConstant;
+import com.persagy.common.enums.ResponseCode;
+import com.persagy.common.utils.ResponseResult;
+import com.persagy.common.utils.ResponseResultUtil;
+
+import cn.hutool.core.collection.CollectionUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 集团签约产品
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021-05-07 12:02:40
+ */
+@RestController
+@SuppressWarnings("unchecked")
+@Api(tags = "产品集团签约管理")
+@RequestMapping(value = "/groupProduct", method = RequestMethod.POST)
+public class SaasGroupProductController {
+	
+    @Autowired
+    private ISaasGroupProductService saasGroupProductService;
+    
+    @Autowired
+    private ISaasProjectProductService saasProjectProductService;
+    
+    /**
+     * 查询产品集团签约信息
+     */
+    @ApiOperation(value = "普通列表查询")
+    @RequestMapping(value = "querySaasGroupProductList")
+    public ResponseResult querySaasGroupProductList(@RequestBody @Validated SaasGroupProductQueryVO model) {
+    	LambdaQueryWrapper<SaasGroupProduct> queryWrapper = new SaasGroupProduct.BuilderQueryWrapper()
+    			.groupCodeEq(model.getGroupCode()).productIdEq(model.getProductId()).validLastEq(model.getValidLast()).builder();
+    	List<SaasGroupProduct> list = this.saasGroupProductService.list(queryWrapper);
+    	if (CollectionUtil.isEmpty(list)) {
+			return ResponseResultUtil.successResult(Lists.newArrayList(), 0L);
+		}
+        return ResponseResultUtil.successResult(list, (long)list.size());
+    }
+    
+    /**
+     * 分页查询产品集团签约信息
+     */
+	@ApiOperation(value = "分页列表查询")
+    @RequestMapping(value = "queryGroupProductPageList")
+    public ResponseResult queryGroupProductPageList(@RequestBody @Validated SaasGroupProductPageVO model) {
+		Page<SaasGroupProduct> page = new Page<SaasGroupProduct>(model.getPage(), model.getSize());
+		LambdaQueryWrapper<SaasGroupProduct> queryWrapper = new SaasGroupProduct.BuilderQueryWrapper()
+    			.groupCodeEq(model.getGroupCode()).productIdEq(model.getProductId()).validLastEq(model.getValidLast()).builder();
+		queryWrapper.orderByDesc(SaasGroupProduct::getProductContract, SaasGroupProduct::getId);		// 默认签约时间降序
+		Page<SaasGroupProduct> pageList = this.saasGroupProductService.page(page, queryWrapper);
+    	if (pageList == null || CollectionUtil.isEmpty(pageList.getRecords())) {
+    		return ResponseResultUtil.successResult(Lists.newArrayList(), 0L);
+		}
+    	List<SaasGroupProduct> records = page.getRecords();
+        if (model.isTotalProject()) {
+			for (SaasGroupProduct saasGroupProduct : records) {
+				LambdaQueryWrapper<SaasProjectProduct> projectWrapper = new SaasProjectProduct.BuilderQueryWrapper().groupCodeEq(saasGroupProduct.getGroupCode()).productIdEq(saasGroupProduct.getProductId()).builder();
+				saasGroupProduct.setProjectNum(this.saasProjectProductService.count(projectWrapper));
+			}
+		}
+    	return ResponseResultUtil.successResult(records, pageList.getTotal());
+    }
+
+    /**
+     * 新增产品集团签约信息
+     */
+	@ApiOperation(value = "保存")
+	@RequestMapping(value = "createSaasGroupProduct")
+    public ResponseResult createSaasGroupProduct(@RequestBody @Validated SaasGroupProductCreateVO model) {
+		if (SaasCommonConstant.STR_STATUS_0.equals(model.getValidLast())) {
+			if (!(model.getValidStartTime() != null && model.getValidEndTime() != null)) {
+				return ResponseResultUtil.errorResult(ResponseCode.C0341.getCode(), "当validLast为0时,签约有效期时间字段不可为空");
+			}
+		}
+		SaasGroupProduct saasGroupProduct = new SaasGroupProduct();
+		BeanUtils.copyProperties(model, saasGroupProduct);
+		saasGroupProduct.setId(null);
+		saasGroupProduct.setUpdateUser(model.getAccountId());
+		boolean result = this.saasGroupProductService.createSaasGroupProduct(saasGroupProduct, model.getPageIds());
+        return result ? ResponseResultUtil.successResult("保存成功", saasGroupProduct.getId()) : ResponseResultUtil.errorResult("保存失败");
+    }
+    
+    /**
+     * 更新产品集团签约信息
+     */
+	@ApiOperation(value = "更新")
+	@RequestMapping(value = "updateSaasGroupProduct")
+    public ResponseResult updateSaasGroupProduct(@RequestBody @Validated SaasGroupProductCreateVO model) {
+		if (SaasCommonConstant.STR_STATUS_0.equals(model.getValidLast())) {
+			if (!(model.getValidStartTime() != null && model.getValidEndTime() != null)) {
+				return ResponseResultUtil.errorResult(ResponseCode.C0341.getCode(), "当validLast为0时,签约有效期时间字段不可为空");
+			}
+		}
+		SaasGroupProduct saasGroupProduct = new SaasGroupProduct();
+		BeanUtils.copyProperties(model, saasGroupProduct);
+		saasGroupProduct.setUpdateUser(model.getAccountId());
+        boolean result = this.saasGroupProductService.updateSaasGroupProduct(saasGroupProduct, model.getPageIds());
+        return result ? ResponseResultUtil.successResult("更新成功") : ResponseResultUtil.errorResult("更新失败");
+    }
+
+    /**
+     * 物理删除产品集团签约信息
+     */
+    @ApiOperation(value = "删除")
+    @RequestMapping(value = "deleteSaasGroupProduct")
+    public ResponseResult deleteSaasGroupProduct(@RequestBody @Validated SaasGroupProductDeleteVO model) {
+    	// 获取签约ID
+    	LambdaQueryWrapper<SaasGroupProduct> queryWrapper = new SaasGroupProduct.BuilderQueryWrapper()
+    			.groupCodeEq(model.getGroupCode()).productIdEq(model.getProductId()).builder();
+    	SaasGroupProduct saasGroupProduct = this.saasGroupProductService.getOne(queryWrapper);
+    	if (saasGroupProduct == null) {
+    		return ResponseResultUtil.errorResult(ResponseCode.C0320.getCode(), "该集团未签约此产品");
+		}
+        boolean result = this.saasGroupProductService.deleteSaasGroupProduct(saasGroupProduct.getId());
+        return result ? ResponseResultUtil.successResult("删除成功") : ResponseResultUtil.errorResult("删除失败");
+    }
+    
+}

+ 70 - 7
saas-account/src/main/java/com/persagy/account/controller/SaasMenuController.java

@@ -15,17 +15,18 @@ import org.springframework.web.bind.annotation.RestController;
 
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Lists;
-import com.persagy.common.enums.ResponseCode;
-import com.persagy.common.utils.ResponseResult;
-import com.persagy.common.utils.ResponseResultUtil;
-import com.persagy.common.utils.StringUtil;
 import com.persagy.account.manage.SaasRoleMenuHandler;
 import com.persagy.account.manage.SaasRoleMenuHandler;
 import com.persagy.account.pojo.dto.SaasMenu;
 import com.persagy.account.pojo.dto.SaasMenu;
 import com.persagy.account.pojo.vo.menu.SaasMenuCreateVO;
 import com.persagy.account.pojo.vo.menu.SaasMenuCreateVO;
 import com.persagy.account.pojo.vo.menu.SaasMenuDeleteVO;
 import com.persagy.account.pojo.vo.menu.SaasMenuDeleteVO;
 import com.persagy.account.pojo.vo.menu.SaasMenuQueryVO;
 import com.persagy.account.pojo.vo.menu.SaasMenuQueryVO;
+import com.persagy.account.pojo.vo.menu.SaasMenuSortVO;
 import com.persagy.account.service.ISaasMenuService;
 import com.persagy.account.service.ISaasMenuService;
 import com.persagy.account.service.ISaasRoleMenuService;
 import com.persagy.account.service.ISaasRoleMenuService;
+import com.persagy.common.enums.ResponseCode;
+import com.persagy.common.utils.ResponseResult;
+import com.persagy.common.utils.ResponseResultUtil;
+import com.persagy.common.utils.StringUtil;
 
 
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.Api;
@@ -64,6 +65,19 @@ public class SaasMenuController {
     }
     }
     
     
     /**
     /**
+     * 查询顶级菜单,菜单配置使用
+     */
+    @ApiOperation(value = "查询顶级菜单")
+    @RequestMapping(value = "querySaasMenuTop")
+    public ResponseResult querySaasMenuTop(@RequestBody @Validated SaasMenuQueryVO queryVO) {
+    	LambdaQueryWrapper<SaasMenu> queryWrapper = new SaasMenu.Builder().createQueryWrapper()
+    			.appIdEq(queryVO.getAppId()).menuTypeEq(queryVO.getMenuType()).builderQueryWrapper();
+    	queryWrapper.apply("(PARENT_ID IS NULL OR TRIM(PARENT_ID) = '')");
+    	List<SaasMenu> menuTree = this.saasMenuService.list(queryWrapper);
+    	return ResponseResultUtil.successResult(menuTree);
+    }
+    
+    /**
      * 查询菜单功能树,角色配置使用
      * 查询菜单功能树,角色配置使用
      */
      */
     @ApiOperation(value = "查询菜单功能树")
     @ApiOperation(value = "查询菜单功能树")
@@ -89,7 +103,7 @@ public class SaasMenuController {
 			}
 			}
 		}
 		}
 		List<SaasMenu> parents = topMenus.stream().sorted(Comparator.comparing(SaasMenu::getMenuSort)).collect(Collectors.toList());
 		List<SaasMenu> parents = topMenus.stream().sorted(Comparator.comparing(SaasMenu::getMenuSort)).collect(Collectors.toList());
-		this.saasRoleMenuHandler.setMenuChildrens(null, menuTree, parents, true, true);
+		this.saasRoleMenuHandler.setMenuChildrens(null, queryVO.getGroupCode(), queryVO.getProjectId(), menuTree, parents, true, !queryVO.isMenu());
     	return ResponseResultUtil.successResult(parents);
     	return ResponseResultUtil.successResult(parents);
     }
     }
     
     
@@ -102,11 +116,16 @@ public class SaasMenuController {
 		if (StringUtil.isNotBlank(createVO.getMenuUrl()) && StringUtil.isEmpty(createVO.getUrlType())) {
 		if (StringUtil.isNotBlank(createVO.getMenuUrl()) && StringUtil.isEmpty(createVO.getUrlType())) {
 			return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "当菜单URL存在时,URL类型不可为空");
 			return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "当菜单URL存在时,URL类型不可为空");
 		}
 		}
+		// 应用下菜单编码唯一
+		ResponseResult validRequestParam = this.validRequestParam(createVO.getAppId(), createVO.getMenuCode(), null);
+		if (validRequestParam != null) {
+			return validRequestParam;
+		}
 		SaasMenu saasMenu = new SaasMenu();
 		SaasMenu saasMenu = new SaasMenu();
 		BeanUtils.copyProperties(createVO, saasMenu);
 		BeanUtils.copyProperties(createVO, saasMenu);
 		saasMenu.setId(null);
 		saasMenu.setId(null);
 		saasMenu.setUpdateUser(createVO.getAccountId());
 		saasMenu.setUpdateUser(createVO.getAccountId());
-        boolean result = this.saasMenuService.save(saasMenu);
+		boolean result = this.saasMenuService.createSaasMenu(saasMenu);
         return result ? ResponseResultUtil.successResult("保存成功", saasMenu.getId()) : ResponseResultUtil.errorResult("保存失败");
         return result ? ResponseResultUtil.successResult("保存成功", saasMenu.getId()) : ResponseResultUtil.errorResult("保存失败");
     }
     }
     
     
@@ -122,6 +141,11 @@ public class SaasMenuController {
 		if (StringUtil.isNotBlank(createVO.getMenuUrl()) && StringUtil.isEmpty(createVO.getUrlType())) {
 		if (StringUtil.isNotBlank(createVO.getMenuUrl()) && StringUtil.isEmpty(createVO.getUrlType())) {
 			return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "当菜单URL存在时,URL类型不可为空");
 			return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "当菜单URL存在时,URL类型不可为空");
 		}
 		}
+		// 应用下菜单编码唯一
+		ResponseResult validRequestParam = this.validRequestParam(createVO.getAppId(), createVO.getMenuCode(), createVO.getId());
+		if (validRequestParam != null) {
+			return validRequestParam;
+		}
 		SaasMenu saasMenu = new SaasMenu();
 		SaasMenu saasMenu = new SaasMenu();
 		BeanUtils.copyProperties(createVO, saasMenu);
 		BeanUtils.copyProperties(createVO, saasMenu);
 		saasMenu.setUpdateUser(createVO.getAccountId());
 		saasMenu.setUpdateUser(createVO.getAccountId());
@@ -140,8 +164,47 @@ public class SaasMenuController {
     	if (!result) {
     	if (!result) {
     		return ResponseResultUtil.errorResult(ResponseCode.A0402.getCode(), "该菜单已被使用,不允许被删除");
     		return ResponseResultUtil.errorResult(ResponseCode.A0402.getCode(), "该菜单已被使用,不允许被删除");
 		}
 		}
-    	result = this.saasRoleMenuHandler.deleteSaasMenu(deleteVO.getId());
+    	SaasMenu saasMenu = this.saasMenuService.getById(deleteVO.getId());
+    	if (saasMenu == null) {
+    		return ResponseResultUtil.errorResult(ResponseCode.C0320.getCode(), "此菜单不存在");
+		}
+    	boolean updateProduct = false;
+    	if (StringUtil.isNotBlank(saasMenu.getProductId())) {
+			// 判断此产品是否仅被此菜单使用
+    		LambdaQueryWrapper<SaasMenu> queryWrapper = new SaasMenu.Builder().createQueryWrapper().productId(saasMenu.getProductId()).builderQueryWrapper();
+    		int count = this.saasMenuService.count(queryWrapper);
+    		if (count < 2) {
+    			updateProduct = true;
+			}
+		}
+    	result = this.saasRoleMenuHandler.deleteSaasMenu(deleteVO.getId(), saasMenu.getProductId(), updateProduct);
         return result ? ResponseResultUtil.successResult("删除成功") : ResponseResultUtil.errorResult("删除失败");
         return result ? ResponseResultUtil.successResult("删除成功") : ResponseResultUtil.errorResult("删除失败");
     }
     }
     
     
+    /**
+     * 菜单顺序调整
+     */
+    @ApiOperation(value = "菜单顺序调整")
+    @RequestMapping(value = "sortSaasMenu")
+    public ResponseResult sortSaasMenu(@RequestBody @Validated SaasMenuSortVO model) {
+    	boolean result = this.saasMenuService.sortSaasMenu(model.getMenus());
+        return result ? ResponseResultUtil.successResult("调整成功") : ResponseResultUtil.errorResult("调整失败");
+    }
+    
+    /**
+     * 
+     * @return
+     */
+    private ResponseResult validRequestParam(String appId, String menuCode, String menuId) {
+    	if (StringUtil.isBlank(appId)) {
+			return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "应用ID,不可为空");
+		}
+		if (StringUtil.isNotBlank(menuCode)) {
+			boolean validMenuCode = this.saasMenuService.validMenuCode(appId, menuCode, menuId);
+			if (!validMenuCode) {
+				return ResponseResultUtil.errorResult(ResponseCode.A0402.getCode(), "此编码已存在");
+			}
+		}
+    	return null;
+    }
 }
 }

+ 9 - 2
saas-account/src/main/java/com/persagy/account/controller/SaasProductController.java

@@ -40,6 +40,7 @@ import io.swagger.annotations.ApiOperation;
 @SuppressWarnings("unchecked")
 @SuppressWarnings("unchecked")
 @RequestMapping(value = "/product", method = RequestMethod.POST)
 @RequestMapping(value = "/product", method = RequestMethod.POST)
 public class SaasProductController {
 public class SaasProductController {
+	
     @Autowired
     @Autowired
     private ISaasProductService saasProductService;
     private ISaasProductService saasProductService;
     
     
@@ -52,7 +53,7 @@ public class SaasProductController {
     	LambdaQueryWrapper<SaasProduct> queryWrapper = new SaasProduct.BuilderQueryWrapper()
     	LambdaQueryWrapper<SaasProduct> queryWrapper = new SaasProduct.BuilderQueryWrapper()
     			.productCodeEq(model.getProductCode()).productNameLike(model.getProductName())
     			.productCodeEq(model.getProductCode()).productNameLike(model.getProductName())
     			.productStateEq(model.getProductState()).productMenuEq(model.getProductMenu())
     			.productStateEq(model.getProductState()).productMenuEq(model.getProductMenu())
-    			.updateUserEq(model.getUpdateUser()).builder();
+    			.appIdEq(model.getAppId()).updateUserEq(model.getUpdateUser()).builder();
     	queryWrapper.orderByDesc(SaasProduct::getUpdateTime, SaasProduct::getId);		// 默认更新时间降序
     	queryWrapper.orderByDesc(SaasProduct::getUpdateTime, SaasProduct::getId);		// 默认更新时间降序
     	List<SaasProduct> list = this.saasProductService.list(queryWrapper);
     	List<SaasProduct> list = this.saasProductService.list(queryWrapper);
     	if (CollectionUtil.isEmpty(list)) {
     	if (CollectionUtil.isEmpty(list)) {
@@ -71,7 +72,7 @@ public class SaasProductController {
     	LambdaQueryWrapper<SaasProduct> queryWrapper = new SaasProduct.BuilderQueryWrapper()
     	LambdaQueryWrapper<SaasProduct> queryWrapper = new SaasProduct.BuilderQueryWrapper()
     			.productCodeEq(pageVO.getProductCode()).productNameLike(pageVO.getProductName())
     			.productCodeEq(pageVO.getProductCode()).productNameLike(pageVO.getProductName())
     			.productStateEq(pageVO.getProductState()).productMenuEq(pageVO.getProductMenu())
     			.productStateEq(pageVO.getProductState()).productMenuEq(pageVO.getProductMenu())
-    			.updateUserEq(pageVO.getUpdateUser()).builder();
+    			.appIdEq(pageVO.getAppId()).updateUserEq(pageVO.getUpdateUser()).builder();
     	queryWrapper.orderByDesc(SaasProduct::getUpdateTime, SaasProduct::getId);		// 默认更新时间降序
     	queryWrapper.orderByDesc(SaasProduct::getUpdateTime, SaasProduct::getId);		// 默认更新时间降序
     	Page<SaasProduct> pageList = this.saasProductService.page(page, queryWrapper);
     	Page<SaasProduct> pageList = this.saasProductService.page(page, queryWrapper);
     	if (pageList == null || CollectionUtil.isEmpty(pageList.getRecords())) {
     	if (pageList == null || CollectionUtil.isEmpty(pageList.getRecords())) {
@@ -86,6 +87,9 @@ public class SaasProductController {
 	@ApiOperation(value = "保存")
 	@ApiOperation(value = "保存")
 	@RequestMapping(value = "createSaasProduct")
 	@RequestMapping(value = "createSaasProduct")
     public ResponseResult createSaasProduct(@RequestBody @Validated SaasProductCreateVO model) {
     public ResponseResult createSaasProduct(@RequestBody @Validated SaasProductCreateVO model) {
+		if (StringUtil.isBlank(model.getAppId())) {
+			return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "应用ID,不可为空"); 
+		}
 		// 验证产品名称、产品编码的唯一性
 		// 验证产品名称、产品编码的唯一性
 		boolean result = this.saasProductService.validProductCode(model.getProductCode(), null);
 		boolean result = this.saasProductService.validProductCode(model.getProductCode(), null);
 		if (!result) {
 		if (!result) {
@@ -112,6 +116,9 @@ public class SaasProductController {
 		if (StringUtil.isBlank(model.getId())) {
 		if (StringUtil.isBlank(model.getId())) {
 			return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "产品ID,不可为空"); 
 			return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "产品ID,不可为空"); 
 		}
 		}
+		if (StringUtil.isBlank(model.getAppId())) {
+			return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "应用ID,不可为空"); 
+		}
 		// 验证产品名称、产品编码的唯一性
 		// 验证产品名称、产品编码的唯一性
 		boolean result = this.saasProductService.validProductCode(model.getProductCode(), model.getId());
 		boolean result = this.saasProductService.validProductCode(model.getProductCode(), model.getId());
 		if (!result) {
 		if (!result) {

+ 130 - 0
saas-account/src/main/java/com/persagy/account/controller/SaasProductPageController.java

@@ -0,0 +1,130 @@
+package com.persagy.account.controller;
+
+import java.util.List;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.persagy.account.manage.SaasProductHandler;
+import com.persagy.account.pojo.dto.SaasProduct;
+import com.persagy.account.pojo.dto.SaasProductPage;
+import com.persagy.account.pojo.vo.product.SaasProductPageCreateVO;
+import com.persagy.account.pojo.vo.product.SaasProductPageDeleteVO;
+import com.persagy.account.pojo.vo.product.SaasProductPageTreeVO;
+import com.persagy.account.service.ISaasProductPageService;
+import com.persagy.account.service.ISaasProductService;
+import com.persagy.common.constant.SaasCommonConstant;
+import com.persagy.common.enums.ResponseCode;
+import com.persagy.common.utils.ResponseResult;
+import com.persagy.common.utils.ResponseResultUtil;
+import com.persagy.common.utils.StringUtil;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 产品页面配置
+ * 
+ * @version 1.0.0
+ * @company persagy
+ * @author zhangqiankun
+ * @date 2021-05-06 10:12:47
+ */
+@RestController
+@Api(tags = "产品页面配置")
+@RequestMapping(value = "/productPage", method = RequestMethod.POST)
+public class SaasProductPageController {
+
+	@Autowired
+	private SaasProductHandler saasProductHandler;
+
+    @Autowired
+    private ISaasProductService saasProductService;
+    
+	@Autowired
+	private ISaasProductPageService saasProductPageService;
+
+	/**
+	 * 查询产品页面树,不涉及权限过滤
+	 */
+	@ApiOperation(value = "查询产品页面树")
+	@RequestMapping(value = "querySaasProductPageTree")
+	public ResponseResult querySaasProductPageTree(@RequestBody @Validated SaasProductPageTreeVO treeVO) {
+		// 验证产品信息是否存在
+		SaasProduct saasProduct = this.saasProductService.getById(treeVO.getProductId());
+		if (saasProduct == null) {
+			return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "产品信息不存在");
+		}
+		// 获取该产品的下级页面权限信息,按照超管逻辑处理
+		List<SaasProductPage> pageList = this.saasProductHandler.querySaasProductPageTree(treeVO.getAngle(), treeVO.getProductId(), null, null, true, treeVO.isShowPageAuth());
+		saasProduct.setPages(pageList);
+		return ResponseResultUtil.successResult(saasProduct);
+	}
+
+	/**
+	 * 新增产品页面信息
+	 */
+	@ApiOperation(value = "保存")
+	@RequestMapping(value = "createSaasProductPage")
+	public ResponseResult createSaasProductPage(@RequestBody @Validated SaasProductPageCreateVO model) {
+		// 验证产品信息是否存在
+		SaasProduct saasProduct = this.saasProductService.getById(model.getProductId());
+		if (saasProduct == null) {
+			return ResponseResultUtil.errorResult(ResponseCode.C0320.getCode(), "产品信息不存在");
+		}
+		SaasProductPage saasProductPage = new SaasProductPage();
+		BeanUtils.copyProperties(model, saasProductPage);
+		saasProductPage.setId(null);
+		saasProductPage.setUpdateUser(model.getAccountId());
+		
+		boolean result = this.saasProductPageService.save(saasProductPage);
+		return result ? ResponseResultUtil.successResult("保存成功", saasProductPage.getId()) : ResponseResultUtil.errorResult("保存失败");
+	}
+
+	/**
+	 * 更新产品页面信息
+	 */
+	@ApiOperation(value = "更新")
+	@RequestMapping(value = "updateSaasProductPage")
+	public ResponseResult updateSaasProductPage(@RequestBody @Validated SaasProductPageCreateVO model) {
+		if (StringUtil.isBlank(model.getId())) {
+			return ResponseResultUtil.errorResult(ResponseCode.A0400.getCode(), "页面ID,不可为空");
+		}
+		// 验证产品信息是否存在
+		SaasProduct saasProduct = this.saasProductService.getById(model.getProductId());
+		if (saasProduct == null) {
+			return ResponseResultUtil.errorResult(ResponseCode.C0320.getCode(), "产品信息不存在");
+		}
+		SaasProductPage saasProductPage = new SaasProductPage();
+		BeanUtils.copyProperties(model, saasProductPage);
+		saasProductPage.setUpdateUser(model.getAccountId());
+		
+		boolean result = this.saasProductPageService.updateById(saasProductPage);
+		return result ? ResponseResultUtil.successResult("更新成功") : ResponseResultUtil.errorResult("更新失败");
+	}
+
+	/**
+	 * 物理删除,落地页不允许删除,请优先变更为非落地页,级联删除此页面的权限点信息
+	 */
+	@ApiOperation(value = "删除")
+	@RequestMapping(value = "deleteSaasProductPage")
+	public ResponseResult deleteSaasProductPage(@RequestBody @Validated SaasProductPageDeleteVO model) {
+		// 判断是否为落地页
+		SaasProductPage productPage = this.saasProductPageService.getById(model.getId());
+		if (productPage == null) {
+			return ResponseResultUtil.errorResult(ResponseCode.C0320.getCode(), "产品页面信息不存在");
+		}
+		if (!SaasCommonConstant.STR_STATUS_0.equals(productPage.getPageFloor())) {
+			return ResponseResultUtil.errorResult(ResponseCode.A0402.getCode(), "落地页不允许删除,请优先变更落地页所属");
+		}
+		// 删除,级联删除权限及其关联关系
+		boolean result = this.saasProductHandler.deleteSaasProductPage(model.getId());
+		return result ? ResponseResultUtil.successResult("删除成功") : ResponseResultUtil.errorResult("删除失败");
+	}
+
+}

+ 1 - 1
saas-account/src/main/java/com/persagy/account/controller/SaasProjectController.java

@@ -173,7 +173,7 @@ public class SaasProjectController {
 			return ResponseResultUtil.errorResult(ResponseCode.A0402.getCode(), "项目ID集合数量过大,请分批查询");
 			return ResponseResultUtil.errorResult(ResponseCode.A0402.getCode(), "项目ID集合数量过大,请分批查询");
 		}
 		}
     	// 账号数据可见性的权限鉴定
     	// 账号数据可见性的权限鉴定
-		Map<String, String> authMap = this.saasAuthHandler.validGroupProjectAuth(queryVO.getAccountId(), queryVO.getGroupCode());
+		Map<String, String> authMap = this.saasAuthHandler.validAccountAuth(queryVO.getAccountId(), queryVO.getGroupCode());
 		if (StringUtil.isNotBlank(authMap.get(SaasCommonConstant.GROUP_CODE))) {
 		if (StringUtil.isNotBlank(authMap.get(SaasCommonConstant.GROUP_CODE))) {
 			queryVO.setGroupCode(authMap.get(SaasCommonConstant.GROUP_CODE));
 			queryVO.setGroupCode(authMap.get(SaasCommonConstant.GROUP_CODE));
 		}
 		}

+ 113 - 0
saas-account/src/main/java/com/persagy/account/controller/SaasProjectProductController.java

@@ -0,0 +1,113 @@
+package com.persagy.account.controller;
+
+import java.util.List;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.google.common.collect.Lists;
+import com.persagy.account.pojo.dto.SaasProjectProduct;
+import com.persagy.account.pojo.vo.product.SaasProjectProductBatchVO;
+import com.persagy.account.pojo.vo.product.SaasProjectProductCreateVO;
+import com.persagy.account.pojo.vo.product.SaasProjectProductQueryVO;
+import com.persagy.account.service.ISaasProjectProductService;
+import com.persagy.common.utils.ResponseResult;
+import com.persagy.common.utils.ResponseResultUtil;
+
+import cn.hutool.core.collection.CollectionUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * 项目签约产品
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021-05-07 12:02:40
+ */
+
+@Api(tags = "产品项目签约管理")
+@RestController
+@RequestMapping(value = "/projectProduct", method = RequestMethod.POST)
+public class SaasProjectProductController {
+	
+    @Autowired
+    private ISaasProjectProductService saasProjectProductService;
+    
+    /**
+     * 查询产品项目签约信息
+     */
+    @ApiOperation(value = "列表查询")
+    @RequestMapping(value = "querySaasProjectProductList")
+    public ResponseResult querySaasProjectProductList(@RequestBody @Validated SaasProjectProductQueryVO model) {
+    	List<SaasProjectProduct> list = null;
+    	if (model.isShowProjectName()) {
+    		list = this.saasProjectProductService.querySaasProjectProductList(model.getContractId(), model.getGroupCode(), model.getProjectId(), model.getProductId());
+		} else {
+			LambdaQueryWrapper<SaasProjectProduct> queryWrapper = new SaasProjectProduct.BuilderQueryWrapper()
+	    			.groupCodeEq(model.getGroupCode()).projectIdEq(model.getProjectId()).productIdEq(model.getProductId()).builder();
+	    	list = this.saasProjectProductService.list(queryWrapper);
+		}
+    	if (CollectionUtil.isEmpty(list)) {
+			return ResponseResultUtil.successResult(Lists.newArrayList(), 0L);
+		}
+        return ResponseResultUtil.successResult(list, (long)list.size());
+    }
+
+    /**
+     * 新增产品项目签约信息
+     */
+	@ApiOperation(value = "保存")
+	@RequestMapping(value = "createSaasProjectProduct")
+    public ResponseResult createSaasProjectProduct(@RequestBody @Validated SaasProjectProductCreateVO model) {
+		SaasProjectProduct saasProjectProduct = new SaasProjectProduct();
+		BeanUtils.copyProperties(model, saasProjectProduct);
+        boolean result = this.saasProjectProductService.save(saasProjectProduct);
+        return result ? ResponseResultUtil.successResult("保存成功") : ResponseResultUtil.errorResult("保存失败");
+    }
+	
+    /**
+     * 批量更新产品项目签约信息,删除新增
+     */
+	@ApiOperation(value = "替换")
+	@RequestMapping(value = "replaceSaasProjectProduct")
+    public ResponseResult replaceSaasProjectProduct(@RequestBody @Validated SaasProjectProductBatchVO model) {
+        boolean result = this.saasProjectProductService.replaceSaasProjectProduct(model.getContractId(), model.getGroupCode(), model.getProductId(), model.getProjectIds());
+        return result ? ResponseResultUtil.successResult("保存成功") : ResponseResultUtil.errorResult("保存失败");
+    }
+    
+    /**
+     * 更新产品项目签约信息
+     */
+	@ApiOperation(value = "更新")
+	@RequestMapping(value = "updateSaasProjectProduct")
+    public ResponseResult updateSaasProjectProduct(@RequestBody @Validated SaasProjectProductCreateVO model) {
+		SaasProjectProduct saasProjectProduct = new SaasProjectProduct();
+		BeanUtils.copyProperties(model, saasProjectProduct);
+		LambdaUpdateWrapper<SaasProjectProduct> updateWrapper = new SaasProjectProduct.BuilderUpdateWrapper()
+				.groupCodeEq(model.getGroupCode()).projectIdEq(model.getProjectId()).productIdEq(model.getProductId()).builder();
+        boolean result = this.saasProjectProductService.update(saasProjectProduct, updateWrapper);
+        return result ? ResponseResultUtil.successResult("更新成功") : ResponseResultUtil.errorResult("更新失败");
+    }
+
+    /**
+     * 物理删除产品项目签约信息
+     */
+    @ApiOperation(value = "删除")
+    @RequestMapping(value = "deleteSaasProjectProduct")
+    public ResponseResult deleteSaasProjectProduct(@RequestBody @Validated SaasProjectProductQueryVO model) {
+    	LambdaQueryWrapper<SaasProjectProduct> queryWrapper = new SaasProjectProduct.BuilderQueryWrapper()
+    			.groupCodeEq(model.getGroupCode()).projectIdEq(model.getProjectId()).productIdEq(model.getProductId()).builder();
+        boolean result = this.saasProjectProductService.remove(queryWrapper);
+        return result ? ResponseResultUtil.successResult("删除成功") : ResponseResultUtil.errorResult("删除失败");
+    }
+    
+}

+ 17 - 65
saas-account/src/main/java/com/persagy/account/manage/SaasAuthHandler.java

@@ -49,16 +49,17 @@ public class SaasAuthHandler {
 	private RedisTemplate<String, Object> redisTemplate;
 	private RedisTemplate<String, Object> redisTemplate;
     
     
     /**
     /**
-     * 验证此账号的集团项目权限
+     * 验证此账号的集团权限
      * 
      * 
      * @param accountId
      * @param accountId
      * @param groupCode
      * @param groupCode
      * @return 
      * @return 
      * 	权限鉴定通过时: 
      * 	权限鉴定通过时: 
+     *  有accountbelong 账号所属字段
      * 	若有返回集团编码,则此返回值必须作为代码向下执行的sql条件;
      * 	若有返回集团编码,则此返回值必须作为代码向下执行的sql条件;
      * 	若有返回accountType,需进行 SaasCommonConstant.STR_STATUS_2.equals(accountType) 账号类型的判断
      * 	若有返回accountType,需进行 SaasCommonConstant.STR_STATUS_2.equals(accountType) 账号类型的判断
      */
      */
-    public Map<String, String> validGroupProjectAuth(String accountId, String groupCode) {
+    public Map<String, String> validAccountAuth(String accountId, String groupCode) {
     	// 获取此账号对应的集团权限
     	// 获取此账号对应的集团权限
     	SaasAccount account = this.saasAccountService.getOne(accountId, null, null);
     	SaasAccount account = this.saasAccountService.getOne(accountId, null, null);
     	if (account == null) {
     	if (account == null) {
@@ -73,9 +74,11 @@ public class SaasAuthHandler {
 			if (StringUtil.isNotBlank(groupCode) && !groupCode.equals(allowGroupCode)) {
 			if (StringUtil.isNotBlank(groupCode) && !groupCode.equals(allowGroupCode)) {
 				throw new BusinessException("账号无该集团的访问权限");
 				throw new BusinessException("账号无该集团的访问权限");
 			}
 			}
-			result.put(SaasCommonConstant.GROUP_CODE, allowGroupCode);
+			groupCode = allowGroupCode;
 		}
 		}
+		result.put(SaasCommonConstant.GROUP_CODE, groupCode);
 		result.put(SaasCommonConstant.ACCOUNT_TYPE, accountType);
 		result.put(SaasCommonConstant.ACCOUNT_TYPE, accountType);
+		result.put(SaasCommonConstant.ACCOUNT_BELONG, account.getAccountBelong());
 		return result;
 		return result;
     }
     }
     
     
@@ -88,20 +91,9 @@ public class SaasAuthHandler {
      */
      */
     public List<SaasGroup> getAllowGroupList(String accountId, String groupCode) {
     public List<SaasGroup> getAllowGroupList(String accountId, String groupCode) {
     	// 获取此账号对应的集团权限
     	// 获取此账号对应的集团权限
-    	SaasAccount account = this.saasAccountService.getOne(accountId, null, null);
-    	if (account == null) {
-    		throw new BusinessException("账号信息为空");
-		}
-    	// 添加数据访问控制,账号类型,0-所有集团项目,1-单集团所有项目,2-其他
-    	String accountType = account.getAccountType();
-		// 1时,判断是否有权限
-		if (SaasCommonConstant.STR_STATUS_1.equals(accountType)) {
-			String allowGroupCode = account.getGroupCode();
-			if (StringUtil.isNotBlank(groupCode) && !groupCode.equals(allowGroupCode)) {
-				throw new BusinessException("账号无该集团的访问权限");
-			}
-			groupCode = allowGroupCode;
-		}
+    	Map<String, String> accountAuth = this.validAccountAuth(accountId, groupCode);
+    	groupCode = accountAuth.get(SaasCommonConstant.GROUP_CODE);
+    	String accountType = accountAuth.get(SaasCommonConstant.ACCOUNT_TYPE);
 		return this.getAllowGroupList(accountId, groupCode, accountType);
 		return this.getAllowGroupList(accountId, groupCode, accountType);
     }
     }
     
     
@@ -136,20 +128,9 @@ public class SaasAuthHandler {
      */
      */
     public List<SaasArea> getAllowAreaList(String accountId, String groupCode) {
     public List<SaasArea> getAllowAreaList(String accountId, String groupCode) {
     	// 获取此账号对应的集团权限
     	// 获取此账号对应的集团权限
-    	SaasAccount account = this.saasAccountService.getOne(accountId, null, null);
-    	if (account == null) {
-    		throw new BusinessException("账号信息为空");
-		}
-    	// 添加数据访问控制,账号类型,0-所有集团项目,1-单集团所有项目,2-其他
-    	String accountType = account.getAccountType();
-		// 1时,判断是否有权限
-		if (SaasCommonConstant.STR_STATUS_1.equals(accountType)) {
-			String allowGroupCode = account.getGroupCode();
-			if (StringUtil.isNotBlank(groupCode) && !groupCode.equals(allowGroupCode)) {
-				throw new BusinessException("账号无该集团的访问权限");
-			}
-			groupCode = allowGroupCode;
-		}
+    	Map<String, String> accountAuth = this.validAccountAuth(accountId, groupCode);
+    	groupCode = accountAuth.get(SaasCommonConstant.GROUP_CODE);
+    	String accountType = accountAuth.get(SaasCommonConstant.ACCOUNT_TYPE);
 		return this.getAllowAreaList(accountId, groupCode, accountType);
 		return this.getAllowAreaList(accountId, groupCode, accountType);
     }
     }
     
     
@@ -173,40 +154,6 @@ public class SaasAuthHandler {
     }
     }
     
     
     /**
     /**
-     * 验证此账号的菜单权限
-     * 
-     * @param accountId
-     * @param groupCode
-     * @return 
-     * 	权限鉴定通过时: 
-     *  有accountbelong 账号所属字段
-     * 	若有返回集团编码,则此返回值必须作为代码向下执行的sql条件;
-     * 	若有返回accountType,需进行 SaasCommonConstant.STR_STATUS_2.equals(accountType) 账号类型的判断
-     */
-    public Map<String, String> validSaasMenuAuth(String accountId, String groupCode) {
-    	// 获取此账号对应的集团权限
-    	SaasAccount account = this.saasAccountService.getOne(accountId, null, null);
-    	if (account == null) {
-    		throw new BusinessException("账号信息为空");
-		}
-    	// 添加数据访问控制,账号类型,0-所有集团项目,1-单集团所有项目,2-其他
-    	String accountType = account.getAccountType();
-		// 1时,判断是否有权限
-    	Map<String, String> result = new HashMap<String, String>(2);
-		if (SaasCommonConstant.STR_STATUS_1.equals(accountType)) {
-			String allowGroupCode = account.getGroupCode();
-			if (StringUtil.isNotBlank(groupCode) && !groupCode.equals(allowGroupCode)) {
-				throw new BusinessException("账号无该集团的访问权限");
-			}
-			result.put(SaasCommonConstant.GROUP_CODE, allowGroupCode);
-		}
-		result.put(SaasCommonConstant.ACCOUNT_TYPE, accountType);
-		result.put(SaasCommonConstant.ACCOUNT_BELONG, account.getAccountBelong());
-		return result;
-    }
-    
-    
-    /**
      * 申请授权码,每次重新生成
      * 申请授权码,每次重新生成
      * 
      * 
      * @param clientId
      * @param clientId
@@ -252,6 +199,11 @@ public class SaasAuthHandler {
     	if (StringUtil.isBlank(redirectUrl)) {
     	if (StringUtil.isBlank(redirectUrl)) {
 			return true;
 			return true;
 		}
 		}
+    	// 判断是否为内部客户端,内部不校验url
+    	Boolean inner = (Boolean)this.redisTemplate.opsForHash().get(clientId, SaasCommonConstant.INNER_CLIENT_REDIS_HASH_KEY);
+    	if (BooleanUtil.isTrue(inner)) {
+    		return true;
+		}
 		Object object = this.redisTemplate.opsForHash().get(clientId, SaasCommonConstant.REDIRECT_URL_REDIS_HASH_KEY);
 		Object object = this.redisTemplate.opsForHash().get(clientId, SaasCommonConstant.REDIRECT_URL_REDIS_HASH_KEY);
 		if(object == null) {
 		if(object == null) {
 			return false;
 			return false;

+ 1 - 1
saas-account/src/main/java/com/persagy/account/manage/SaasGroupProjectHandler.java

@@ -167,7 +167,7 @@ public class SaasGroupProjectHandler {
      */
      */
     public Page<SaasProject> querySaasProjectList(SaasProject saasProject, Integer page, Integer size) {
     public Page<SaasProject> querySaasProjectList(SaasProject saasProject, Integer page, Integer size) {
 		// 账号数据可见性的权限鉴定
 		// 账号数据可见性的权限鉴定
-		Map<String, String> authMap = this.saasAuthHandler.validGroupProjectAuth(saasProject.getAccountId(), saasProject.getGroupCode());
+		Map<String, String> authMap = this.saasAuthHandler.validAccountAuth(saasProject.getAccountId(), saasProject.getGroupCode());
 		if (StringUtil.isNotBlank(authMap.get(SaasCommonConstant.GROUP_CODE))) {
 		if (StringUtil.isNotBlank(authMap.get(SaasCommonConstant.GROUP_CODE))) {
 			saasProject.setGroupCode(authMap.get(SaasCommonConstant.GROUP_CODE));
 			saasProject.setGroupCode(authMap.get(SaasCommonConstant.GROUP_CODE));
 		}
 		}

+ 251 - 0
saas-account/src/main/java/com/persagy/account/manage/SaasProductHandler.java

@@ -0,0 +1,251 @@
+package com.persagy.account.manage;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.google.common.collect.Lists;
+import com.persagy.account.pojo.dto.SaasFunction;
+import com.persagy.account.pojo.dto.SaasGroupProduct;
+import com.persagy.account.pojo.dto.SaasMenu;
+import com.persagy.account.pojo.dto.SaasProductPage;
+import com.persagy.account.pojo.dto.SaasProjectProduct;
+import com.persagy.account.service.ISaasFunctionService;
+import com.persagy.account.service.ISaasGroupProductService;
+import com.persagy.account.service.ISaasProductPageService;
+import com.persagy.account.service.ISaasProjectProductService;
+import com.persagy.common.constant.SaasCommonConstant;
+import com.persagy.common.utils.StringUtil;
+
+import cn.hutool.core.collection.CollectionUtil;
+
+/**
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021年5月6日 上午10:26:15
+ */
+@Component
+public class SaasProductHandler {
+	
+    //@Autowired
+    //private ISaasRoleMenuService saasRoleMenuService;
+	
+	@Autowired
+	private SaasAuthHandler saasAuthHandler;
+	
+    @Autowired
+    private ISaasFunctionService saasFunctionService;
+    
+	@Autowired
+	private ISaasProductPageService saasProductPageService;
+	
+    @Autowired
+    private ISaasGroupProductService saasGroupProductService;
+    
+    @Autowired
+    private ISaasProjectProductService saasProjectProductService;
+
+    
+	/**
+	 * 这里未签约,不会返回异常,会设置为空的集合
+	 * 获取此账号下能够可见的页面集合,优先做权限判断,判定是否进行了签约
+	 * 
+	 * @param saasMenu
+	 * @param accountId
+	 * @param projectId
+	 * @param groupCode
+	 * @param isAdmin
+	 */
+	public void queryProductAuthList(SaasMenu saasMenu, String accountId, String projectId, String groupCode, boolean isAdmin, boolean showPageAuth) {
+		// angle 1-集团视角,2-项目视角
+		String angle = StringUtil.isBlank(projectId) ? SaasCommonConstant.STR_STATUS_1 : SaasCommonConstant.STR_STATUS_2;
+		if (StringUtil.isBlank(saasMenu.getProductId())) {
+			// 未挂有产品,关联查询出对应的功能点集合,即可返回
+			List<SaasFunction> functions = null;
+			if (isAdmin) {
+				LambdaQueryWrapper<SaasFunction> queryWrapper = new SaasFunction.Builder().createQueryWrapper().menuIdEq(saasMenu.getMenuId()).builderQueryWrapper();
+				functions = this.saasFunctionService.list(queryWrapper.orderByAsc(SaasFunction::getFunSort));
+			} else {
+				functions = this.saasFunctionService.queryAccountFunctionList(accountId, saasMenu.getGroupCode(), saasMenu.getMenuId());
+			}
+			saasMenu.setFunctions(CollectionUtil.isEmpty(functions) ? Lists.newArrayList() : functions);
+			saasMenu.setFunctions(functions);
+			return;
+		}
+		// 挂有产品,判断是否已签约该产品
+		int count = 0;
+		if (SaasCommonConstant.STR_STATUS_2.equals(angle)) {
+			LambdaQueryWrapper<SaasProjectProduct> queryWrapper = new SaasProjectProduct.BuilderQueryWrapper().groupCodeEq(groupCode).projectIdEq(projectId).productIdEq(saasMenu.getProductId()).builder();
+			count = this.saasProjectProductService.count(queryWrapper);
+		} else {
+			LambdaQueryWrapper<SaasGroupProduct> queryWrapper = new SaasGroupProduct.BuilderQueryWrapper().groupCodeEq(groupCode).productIdEq(saasMenu.getProductId()).builder();
+			count = this.saasGroupProductService.count(queryWrapper);
+		}
+		if (count < 1) {
+			saasMenu.setProductPages(Lists.newArrayList());
+			return;	// 未签约
+		}
+		// 已签约,走关联查询获取页面及其权限
+		List<SaasProductPage> pageTree = this.querySaasProductPageTree(angle, saasMenu.getProductId(), accountId, saasMenu.getMenuId(), isAdmin, showPageAuth);
+		saasMenu.setProductPages(pageTree);
+		return;
+	}
+	
+	/**
+	 * 获取产品页面树
+	 * @param angle
+	 * @param productId
+	 * @param accountId
+	 * @param menuId
+	 * @param isAdmin 当为超管时,accountId、menuId可为空
+	 * @param showPageAuth
+	 * @return 空集合或数据集合
+	 */
+	public List<SaasProductPage> querySaasProductPageTree(String angle, String productId, String accountId, String menuId, boolean isAdmin, boolean showPageAuth) {
+		// 查询出该产品下顶级页面信息集合
+		List<SaasProductPage> topList = this.getTopProductPageList(angle, productId);
+		if (topList.size() == 0) {
+			return topList;
+		}
+		this.querySaasProductPageTree(topList, accountId, menuId, angle, isAdmin, showPageAuth);
+		return topList;
+	}
+	
+	/**
+	 * 获取顶级产品页面信息集合
+	 * @param angle
+	 * @param productId
+	 * @return
+	 */
+	public List<SaasProductPage> getTopProductPageList(String angle, String productId) {
+		LambdaQueryWrapper<SaasProductPage> queryWrapper = new SaasProductPage.BuilderQueryWrapper().pageAngleIn(angle).productIdEq(productId).builder();
+		queryWrapper.last("AND (PARENT_ID IS NULL OR TRIM(PARENT_ID) = '')");
+		List<SaasProductPage> topList = this.saasProductPageService.list(queryWrapper);
+		return CollectionUtil.isEmpty(topList) ? Lists.newArrayList() : topList;
+	}
+	
+	/**
+	 * 封装产品页面树
+	 * @param topList
+	 * @param accountId
+	 * @param menuId
+	 * @param angle 当前视角
+	 * @param isAdmin 是否为超管,true-是,超管时,accountId、menuId均可为空
+	 * @param showPageAuth true-查询出对应页面的权限点信息
+	 */
+	private void querySaasProductPageTree(List<SaasProductPage> topList, String accountId, String menuId, String angle, boolean isAdmin, boolean showPageAuth) {
+		for (SaasProductPage saasProductPage : topList) {
+			List<SaasProductPage> nextList = null;
+			if (isAdmin) {
+				nextList = this.getAdminProductPageAuth(saasProductPage, angle, showPageAuth);
+			} else {
+				nextList = this.getCommonProductPageAuth(saasProductPage, accountId, menuId, angle, showPageAuth);
+			}
+			saasProductPage.setChildrens(nextList);
+			this.querySaasProductPageTree(nextList, accountId, menuId, angle, isAdmin, showPageAuth);
+		}
+	}
+	
+	/**
+	 * 获取超管对应的产品页面权限
+	 * 
+	 * @param saasProductPage
+	 * @param angle
+	 * @param showPageAuth
+	 * @return 空集合
+	 */
+	private List<SaasProductPage> getAdminProductPageAuth(SaasProductPage saasProductPage, String angle, boolean showPageAuth) {
+		if (showPageAuth) {
+			// 获取此页面的权限集合
+			LambdaQueryWrapper<SaasFunction> queryWrapper = new SaasFunction.Builder().createQueryWrapper().pageIdEq(saasProductPage.getId()).builderQueryWrapper();
+			List<SaasFunction> functions = this.saasFunctionService.list(queryWrapper);
+			saasProductPage.setFunctions(functions);
+		}
+		// 获取下级页面
+		LambdaQueryWrapper<SaasProductPage> queryWrapper = new SaasProductPage.BuilderQueryWrapper().pageAngleIn(angle).parentIdEq(saasProductPage.getId()).builder();
+		List<SaasProductPage> list = this.saasProductPageService.list(queryWrapper);
+		return CollectionUtil.isEmpty(list) ? Lists.newArrayList() : list;
+	}
+	
+	/**
+	 * 获取普通用户对应的产品页面权限
+	 * 
+	 * @param saasProductPage
+	 * @param accountId;
+	 * @param menuId
+	 * @param angle
+	 * @param showPageAuth
+	 * @return 空集合
+	 */
+	private List<SaasProductPage> getCommonProductPageAuth(SaasProductPage saasProductPage, String accountId, String menuId, String angle, boolean showPageAuth) {
+		if (showPageAuth) {
+			// 获取此页面的权限集合,走表关联查询
+			List<SaasFunction> functions = this.saasFunctionService.queryProductPageFunctions(accountId, menuId, saasProductPage.getId());
+			saasProductPage.setFunctions(functions);
+		}
+		// 获取下级页面,走表关联查询
+		List<SaasProductPage> list = this.saasProductPageService.queryAccountProductPages(accountId, saasProductPage.getProductId(), menuId, saasProductPage.getId(), angle);
+		return CollectionUtil.isEmpty(list) ? Lists.newArrayList() : list;
+	}
+	
+
+	/**
+	 * 获取此账号下能够可见的页面集合
+	 * 
+	 * @param accountId
+	 * @param productId
+	 * @param angle
+	 * @param groupCode
+	 * @param projectId
+	 * @return
+	 */
+	public List<SaasProductPage> queryProductPageList(String accountId, String productId, String angle, String groupCode, String projectId) {
+		List<SaasProductPage> productPages = null;
+		// 产品ID存在,获取该账户对应的产品页面权限,返回,后续每页的权限,由再次点击时获取
+    	Map<String, String> authMap = this.saasAuthHandler.validAccountAuth(accountId, groupCode);
+		groupCode = authMap.get(SaasCommonConstant.GROUP_CODE);
+		String accountType = authMap.get(SaasCommonConstant.ACCOUNT_TYPE);
+		if (SaasCommonConstant.STR_STATUS_0.equals(accountType)) {
+			// 超管时,直接展示所有当前视角页
+			LambdaQueryWrapper<SaasProductPage> queryWrapper = new SaasProductPage.BuilderQueryWrapper().pageAngleIn(angle).productIdEq(productId).builder();
+			productPages = this.saasProductPageService.list(queryWrapper);
+			return CollectionUtil.isEmpty(productPages) ? Lists.newArrayList() : productPages;
+		}
+		// 非超管时,走产品关联查询
+		if (StringUtil.isNotBlank(projectId)) {
+			// 项目ID,不为空时,或许此项目下所设置的页面
+			productPages = this.saasProductPageService.queryProjectProductPages(groupCode, projectId, productId, angle);
+		} else {
+			// 获取此集团下所设置的页面
+			productPages = this.saasProductPageService.queryGroupProductPages(groupCode, productId, angle);
+		}
+		return CollectionUtil.isEmpty(productPages) ? Lists.newArrayList() : productPages;
+	}
+	
+	
+	/**
+	 * 删除产品页面信息,级联删除页面下权限及其与权限相关的关联关系
+	 * @param id
+	 * @return
+	 */
+	@Transactional
+	public boolean deleteSaasProductPage(String id) {
+		boolean result = this.saasProductPageService.removeById(id);
+		if (result) {
+			// 1.删除权限点信息
+			/*LambdaQueryWrapper<SaasFunction> funWrapper = new SaasFunction.Builder().createQueryWrapper().pageIdEq(id).builderQueryWrapper();
+			this.saasFunctionService.remove(funWrapper);
+			// 2.删除此页面对应的菜单、角色关系数据
+			LambdaQueryWrapper<SaasRoleMenu> roleMenuWrapper = new SaasRoleMenu.Builder().createQueryWrapper().pageIdEq(menuId).builderQueryWrapper();
+			this.saasRoleMenuService.remove(roleMenuWrapper);*/
+		}
+		return result;
+	}
+
+}

+ 36 - 15
saas-account/src/main/java/com/persagy/account/manage/SaasRoleMenuHandler.java

@@ -18,6 +18,7 @@ import com.persagy.common.utils.StringUtil;
 import com.persagy.account.pojo.dto.SaasAccountRole;
 import com.persagy.account.pojo.dto.SaasAccountRole;
 import com.persagy.account.pojo.dto.SaasFunction;
 import com.persagy.account.pojo.dto.SaasFunction;
 import com.persagy.account.pojo.dto.SaasMenu;
 import com.persagy.account.pojo.dto.SaasMenu;
+import com.persagy.account.pojo.dto.SaasProduct;
 import com.persagy.account.pojo.dto.SaasRole;
 import com.persagy.account.pojo.dto.SaasRole;
 import com.persagy.account.pojo.dto.SaasRoleMenu;
 import com.persagy.account.pojo.dto.SaasRoleMenu;
 import com.persagy.account.pojo.vo.auth.SaasMenuFunctionVO;
 import com.persagy.account.pojo.vo.auth.SaasMenuFunctionVO;
@@ -26,6 +27,7 @@ import com.persagy.account.pojo.vo.role.SaasRoleDeleteVO;
 import com.persagy.account.service.ISaasAccountRoleService;
 import com.persagy.account.service.ISaasAccountRoleService;
 import com.persagy.account.service.ISaasFunctionService;
 import com.persagy.account.service.ISaasFunctionService;
 import com.persagy.account.service.ISaasMenuService;
 import com.persagy.account.service.ISaasMenuService;
+import com.persagy.account.service.ISaasProductService;
 import com.persagy.account.service.ISaasRoleMenuService;
 import com.persagy.account.service.ISaasRoleMenuService;
 import com.persagy.account.service.ISaasRoleService;
 import com.persagy.account.service.ISaasRoleService;
 
 
@@ -52,6 +54,12 @@ public class SaasRoleMenuHandler {
 	@Autowired
 	@Autowired
 	private ISaasMenuService saasMenuService;
 	private ISaasMenuService saasMenuService;
 	
 	
+	@Autowired
+	private SaasProductHandler saasProductHandler;
+	
+    @Autowired
+    private ISaasProductService saasProductService;
+    
     @Autowired
     @Autowired
     private ISaasFunctionService saasFunctionService;
     private ISaasFunctionService saasFunctionService;
     
     
@@ -62,13 +70,14 @@ public class SaasRoleMenuHandler {
     private ISaasAccountRoleService saasAccountRoleService;
     private ISaasAccountRoleService saasAccountRoleService;
 	
 	
 	/**
 	/**
-	 * 获取菜单权限树,权限过滤
+	 * 获取菜单权限树,权限过滤,需要判断视角,1-集团视角,2-项目视角 项目ID存在即为项目视角
+	 * 
 	 * @param queryVO
 	 * @param queryVO
 	 * @return 
 	 * @return 
 	 */
 	 */
 	public List<SaasMenu> querySaasMenuTree(SaasMenuQueryVO queryVO) {
 	public List<SaasMenu> querySaasMenuTree(SaasMenuQueryVO queryVO) {
 		// 获取此账号对应的菜单范围
 		// 获取此账号对应的菜单范围
-    	Map<String, String> menuAuth = this.saasAuthHandler.validSaasMenuAuth(queryVO.getAccountId(), queryVO.getGroupCode());
+    	Map<String, String> menuAuth = this.saasAuthHandler.validAccountAuth(queryVO.getAccountId(), queryVO.getGroupCode());
     	if (StringUtil.isNotBlank(menuAuth.get(SaasCommonConstant.GROUP_CODE))) {
     	if (StringUtil.isNotBlank(menuAuth.get(SaasCommonConstant.GROUP_CODE))) {
     		queryVO.setGroupCode(menuAuth.get(SaasCommonConstant.GROUP_CODE));
     		queryVO.setGroupCode(menuAuth.get(SaasCommonConstant.GROUP_CODE));
 		}
 		}
@@ -79,9 +88,9 @@ public class SaasRoleMenuHandler {
     	List<SaasMenu> menuTree = null;
     	List<SaasMenu> menuTree = null;
     	if (SaasCommonConstant.STR_STATUS_0.equals(accountType) && SaasCommonConstant.STR_STATUS_0.equals(accountBelong)) {
     	if (SaasCommonConstant.STR_STATUS_0.equals(accountType) && SaasCommonConstant.STR_STATUS_0.equals(accountBelong)) {
     		// 如果是运维系统的账号,accountType为0的情况下查询所有菜单权限,其余情况走表关联查询,为1时,后续可添加
     		// 如果是运维系统的账号,accountType为0的情况下查询所有菜单权限,其余情况走表关联查询,为1时,后续可添加
-    		menuTree = this.querySaasMenuTree(null, null, queryVO.getAppId(), true, queryVO.isMenu());
+    		menuTree = this.querySaasMenuTree(null, null, queryVO.getProjectId(), queryVO.getAppId(), true, queryVO.isMenu());
     	} else {
     	} else {
-    		menuTree = this.querySaasMenuTree(queryVO.getAccountId(), queryVO.getGroupCode(), queryVO.getAppId(), false, queryVO.isMenu());
+    		menuTree = this.querySaasMenuTree(queryVO.getAccountId(), queryVO.getGroupCode(), queryVO.getProjectId(), queryVO.getAppId(), false, queryVO.isMenu());
 		}
 		}
     	return menuTree;
     	return menuTree;
 	}
 	}
@@ -91,12 +100,13 @@ public class SaasRoleMenuHandler {
 	 * 
 	 * 
 	 * @param accountId
 	 * @param accountId
 	 * @param groupCode
 	 * @param groupCode
+	 * @param projectId
 	 * @param appId
 	 * @param appId
-	 * @param isAdmin 是否为超管 true-是
+	 * @param isAdmin 是否为超管 true-是,此为系统超管
 	 * @param isMenu 是否仅查询出菜单树,true-是
 	 * @param isMenu 是否仅查询出菜单树,true-是
 	 * @return 空集合或数据菜单树
 	 * @return 空集合或数据菜单树
 	 */
 	 */
-	public List<SaasMenu> querySaasMenuTree(String accountId, String groupCode, String appId, boolean isAdmin, boolean isMenu) {
+	public List<SaasMenu> querySaasMenuTree(String accountId, String groupCode, String projectId, String appId, boolean isAdmin, boolean isMenu) {
 		// 1.获取此账号对应菜单集合
 		// 1.获取此账号对应菜单集合
 		List<SaasMenu> menuTree = null;
 		List<SaasMenu> menuTree = null;
 		if (isAdmin) {
 		if (isAdmin) {
@@ -118,24 +128,27 @@ public class SaasRoleMenuHandler {
 			}
 			}
 		}
 		}
 		List<SaasMenu> parents = topMenus.stream().sorted(Comparator.comparing(SaasMenu::getMenuSort)).collect(Collectors.toList());
 		List<SaasMenu> parents = topMenus.stream().sorted(Comparator.comparing(SaasMenu::getMenuSort)).collect(Collectors.toList());
-		this.setMenuChildrens(accountId, menuTree, parents, isAdmin, !isMenu);
+		this.setMenuChildrens(accountId, groupCode, projectId, menuTree, parents, isAdmin, !isMenu);
 		return parents;
 		return parents;
 	}
 	}
 
 
 	/**
 	/**
 	 * 生成菜单树
 	 * 生成菜单树
 	 * @param accountId 为null时,isAdmin 为true
 	 * @param accountId 为null时,isAdmin 为true
+	 * @param angle 1-集团视角,2-项目视角
+	 * @param groupCode
 	 * @param menuTree
 	 * @param menuTree
 	 * @param parents
 	 * @param parents
 	 * @param isAdmin 是否为超管 true-是
 	 * @param isAdmin 是否为超管 true-是
 	 * @param menuAndFun 
 	 * @param menuAndFun 
 	 */
 	 */
-	public void setMenuChildrens(String accountId, List<SaasMenu> menuTree, List<SaasMenu> parents, boolean isAdmin, boolean menuAndFun) {
+	public void setMenuChildrens(String accountId, String groupCode, String projectId, List<SaasMenu> menuTree, List<SaasMenu> parents, boolean isAdmin, boolean menuAndFun) {
 		for (SaasMenu parent : parents) {
 		for (SaasMenu parent : parents) {
 			if (StringUtil.isBlank(parent.getMenuId())) {
 			if (StringUtil.isBlank(parent.getMenuId())) {
 				parent.setMenuId(parent.getId());
 				parent.setMenuId(parent.getId());
 			}
 			}
 			if (menuAndFun && StringUtil.isNotBlank(parent.getMenuUrl())) {
 			if (menuAndFun && StringUtil.isNotBlank(parent.getMenuUrl())) {
+				this.saasProductHandler.queryProductAuthList(parent, accountId, groupCode, projectId, isAdmin, menuAndFun);
 				// 查询出对应功能点集合
 				// 查询出对应功能点集合
 				List<SaasFunction> functions = null;
 				List<SaasFunction> functions = null;
 				if (isAdmin) {
 				if (isAdmin) {
@@ -158,7 +171,7 @@ public class SaasRoleMenuHandler {
 			
 			
 			List<SaasMenu> nextTops = childrens.stream().sorted(Comparator.comparing(SaasMenu::getMenuSort)).collect(Collectors.toList());
 			List<SaasMenu> nextTops = childrens.stream().sorted(Comparator.comparing(SaasMenu::getMenuSort)).collect(Collectors.toList());
 			parent.setChildrens(nextTops);
 			parent.setChildrens(nextTops);
-			this.setMenuChildrens(accountId, menuTree, nextTops, isAdmin, menuAndFun);
+			this.setMenuChildrens(accountId, groupCode, projectId, menuTree, nextTops, isAdmin, menuAndFun);
 		}
 		}
 	}
 	}
 	
 	
@@ -187,12 +200,21 @@ public class SaasRoleMenuHandler {
 	 * 删除菜单,级联删除,角色、菜单、功能三者关联关系数据
 	 * 删除菜单,级联删除,角色、菜单、功能三者关联关系数据
 	 * 
 	 * 
 	 * @param menuId
 	 * @param menuId
+	 * @param productId
+	 * @param updateProduct 是否更新产品关联菜单字段
 	 * @return
 	 * @return
 	 */
 	 */
 	@Transactional
 	@Transactional
-	public boolean deleteSaasMenu(String menuId) {
+	public boolean deleteSaasMenu(String menuId, String productId, boolean updateProduct) {
 		boolean result = this.saasMenuService.removeById(menuId);
 		boolean result = this.saasMenuService.removeById(menuId);
 		if (result) {
 		if (result) {
+			if (updateProduct) {
+				// 更新产品是否已关联菜单字段
+				SaasProduct saasProduct = new SaasProduct();
+				saasProduct.setId(productId);
+				saasProduct.setProductMenu(SaasCommonConstant.STR_STATUS_0);
+				result = this.saasProductService.updateById(saasProduct);
+			}
 			// 1.删除此菜单下功能
 			// 1.删除此菜单下功能
 			LambdaQueryWrapper<SaasFunction> funWrapper = new SaasFunction.Builder().createQueryWrapper().menuIdEq(menuId).builderQueryWrapper();
 			LambdaQueryWrapper<SaasFunction> funWrapper = new SaasFunction.Builder().createQueryWrapper().menuIdEq(menuId).builderQueryWrapper();
 			this.saasFunctionService.remove(funWrapper);
 			this.saasFunctionService.remove(funWrapper);
@@ -205,18 +227,17 @@ public class SaasRoleMenuHandler {
 
 
 	/**
 	/**
 	 * 删除功能,级联删除菜单、功能关系
 	 * 删除功能,级联删除菜单、功能关系
-	 * @param menuId 
 	 * 
 	 * 
-	 * @param id
+	 * @param funId
 	 * @return
 	 * @return
 	 */
 	 */
 	@Transactional
 	@Transactional
-	public boolean deleteSaasFunction(String funId, String menuId) {
-		LambdaQueryWrapper<SaasFunction> queryWrapper = new SaasFunction.Builder().createQueryWrapper().menuIdEq(menuId).idEq(funId).builderQueryWrapper();
+	public boolean deleteSaasFunction(String funId) {
+		LambdaQueryWrapper<SaasFunction> queryWrapper = new SaasFunction.Builder().createQueryWrapper().idEq(funId).builderQueryWrapper();
 		boolean result = this.saasFunctionService.removeById(queryWrapper);
 		boolean result = this.saasFunctionService.removeById(queryWrapper);
 		if (result) {
 		if (result) {
 			// 1.删除此功能对应的菜单、角色关系数据
 			// 1.删除此功能对应的菜单、角色关系数据
-			LambdaQueryWrapper<SaasRoleMenu> roleMenuWrapper = new SaasRoleMenu.Builder().createQueryWrapper().menuIdEq(menuId).functionIdEq(funId).builderQueryWrapper();
+			LambdaQueryWrapper<SaasRoleMenu> roleMenuWrapper = new SaasRoleMenu.Builder().createQueryWrapper().functionIdEq(funId).builderQueryWrapper();
 			this.saasRoleMenuService.remove(roleMenuWrapper);
 			this.saasRoleMenuService.remove(roleMenuWrapper);
 		}
 		}
 		return result;
 		return result;

+ 15 - 3
saas-account/src/main/java/com/persagy/account/mapper/SaasAccountMapper.java

@@ -1,6 +1,7 @@
 package com.persagy.account.mapper;
 package com.persagy.account.mapper;
 
 
 import java.util.List;
 import java.util.List;
+import java.util.Set;
 
 
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Param;
@@ -29,12 +30,23 @@ public interface SaasAccountMapper extends BaseMapper<SaasAccount> {
 	 */
 	 */
 	List<SaasAccount> queryAccountGroupList(@Param("account") SaasAccount saasAccount, @Param("startRow") int startRow, @Param("size") int size);
 	List<SaasAccount> queryAccountGroupList(@Param("account") SaasAccount saasAccount, @Param("startRow") int startRow, @Param("size") int size);
 
 
-    /**
-     * 根据角色ID获取,账号信息
+	/**
+     * 根据角色ID获取账号信息
+     * 
+     * @param saasAccount
+     * @param roleId
+     * @return
+     */
+	List<SaasAccount> queryRoleAccountList(@Param("account") SaasAccount saasAccount, @Param("roleIds") Set<String> roleIds, 
+			@Param("startRow") Integer startRow, @Param("size") Integer size);
+
+	/**
+     * 根据角色ID获取账号信息时,分页查询总数统计
+     * 
      * @param saasAccount
      * @param saasAccount
      * @param roleId
      * @param roleId
      * @return
      * @return
      */
      */
-	List<SaasAccount> queryRoleAccountList(@Param("account") SaasAccount saasAccount, @Param("roleId") String roleId);
+	Integer countRoleAccountList(@Param("account") SaasAccount saasAccount, @Param("roleIds") Set<String> roleIds);
 
 
 }
 }

+ 12 - 1
saas-account/src/main/java/com/persagy/account/mapper/SaasFunctionMapper.java

@@ -36,8 +36,19 @@ public interface SaasFunctionMapper extends BaseMapper<SaasFunction> {
 	 * @param groupCode
 	 * @param groupCode
 	 * @param roleId
 	 * @param roleId
 	 * @param menuId
 	 * @param menuId
+	 * @param pageId
 	 * @return
 	 * @return
 	 */
 	 */
-	List<SaasFunction> queryRoleFunctionList(@Param("groupCode") String groupCode, @Param("roleId") String roleId, @Param("menuId") String menuId);
+	List<SaasFunction> queryRoleFunctionList(@Param("groupCode") String groupCode, @Param("roleId") String roleId, 
+			@Param("menuId") String menuId, @Param("pageId") String pageId);
+	
+	/**
+	 * 获取此产品页面再此账号下所拥有的的页面权限
+	 * @param accountId
+	 * @param menuId
+	 * @param pageId
+	 * @return
+	 */
+	List<SaasFunction> queryProductPageFunctions(@Param("accountId") String accountId, @Param("menuId") String menuId, @Param("pageId") String pageId);
 	
 	
 }
 }

+ 30 - 0
saas-account/src/main/java/com/persagy/account/mapper/SaasGroupProductDetailMapper.java

@@ -0,0 +1,30 @@
+package com.persagy.account.mapper;
+
+import java.util.Set;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.persagy.account.pojo.dto.SaasGroupProductDetail;
+
+/**
+ * 集团签约产品详情
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021-05-07 12:02:40
+ */
+@Mapper
+public interface SaasGroupProductDetailMapper extends BaseMapper<SaasGroupProductDetail> {
+
+	/**
+	 * 创建签约ID,与页面ID的关联关系
+	 * @param contractId
+	 * @param pageIds
+	 * @return
+	 */
+	Integer insertSaasGroupProductDetail(@Param("contractId") String contractId, @Param("pageIds") Set<String> pageIds);
+
+}

+ 19 - 0
saas-account/src/main/java/com/persagy/account/mapper/SaasGroupProductMapper.java

@@ -0,0 +1,19 @@
+package com.persagy.account.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.persagy.account.pojo.dto.SaasGroupProduct;
+
+/**
+ * 集团签约产品
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021-05-07 12:02:40
+ */
+@Mapper
+public interface SaasGroupProductMapper extends BaseMapper<SaasGroupProduct> {
+
+}

+ 9 - 0
saas-account/src/main/java/com/persagy/account/mapper/SaasMenuMapper.java

@@ -7,6 +7,7 @@ import org.apache.ibatis.annotations.Param;
 
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.persagy.account.pojo.dto.SaasMenu;
 import com.persagy.account.pojo.dto.SaasMenu;
+import com.persagy.account.pojo.vo.menu.SaasMenuSort;
 
 
 /**
 /**
  * 菜单信息
  * 菜单信息
@@ -24,6 +25,7 @@ public interface SaasMenuMapper extends BaseMapper<SaasMenu> {
 	 * @param accountId
 	 * @param accountId
 	 * @param groupCode
 	 * @param groupCode
 	 * @param appId
 	 * @param appId
+	 * @param angle
 	 * @param valid
 	 * @param valid
 	 * @return
 	 * @return
 	 */
 	 */
@@ -37,5 +39,12 @@ public interface SaasMenuMapper extends BaseMapper<SaasMenu> {
 	 * @return
 	 * @return
 	 */
 	 */
 	List<SaasMenu> queryMenuAndEmptyFunction(@Param("groupCode") String groupCode, @Param("roleId") String roleId);
 	List<SaasMenu> queryMenuAndEmptyFunction(@Param("groupCode") String groupCode, @Param("roleId") String roleId);
+
+	/**
+	 * 菜单重排序
+	 * @param menus
+	 * @return
+	 */
+	Integer sortSaasMenu(@Param("menus") List<SaasMenuSort> menus);
 	
 	
 }
 }

+ 54 - 0
saas-account/src/main/java/com/persagy/account/mapper/SaasProductPageMapper.java

@@ -0,0 +1,54 @@
+package com.persagy.account.mapper;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.persagy.account.pojo.dto.SaasProductPage;
+
+/**
+ * 产品页面配置
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021-05-06 10:12:47
+ */
+@Mapper
+public interface SaasProductPageMapper extends BaseMapper<SaasProductPage> {
+
+	/**
+	 * 获取集团所签约的页面集合
+	 * 
+	 * @param groupCode
+	 * @param productId
+	 * @param angle
+	 * @return
+	 */
+	List<SaasProductPage> queryGroupProductPages(@Param("groupCode") String groupCode, @Param("productId") String productId, @Param("angle") String angle);
+	
+	/**
+	 * 获取项目所签约的此产品的页面集合
+	 * 
+	 * @param groupCode
+	 * @param projectId
+	 * @param productId
+	 * @param angle
+	 * @return
+	 */
+	List<SaasProductPage> queryProjectProductPages(@Param("groupCode") String groupCode, @Param("projectId") String projectId, @Param("productId") String productId, @Param("angle") String angle);
+
+	/**
+	 * 获取此账号所拥有的页面权限
+	 * @param accountId
+	 * @param menuId
+	 * @param pageId
+	 * @param angle
+	 * @return
+	 */
+	List<SaasProductPage> queryAccountProductPages(@Param("accountId") String accountId, @Param("productId") String productId,
+			@Param("menuId") String menuId, @Param("parentId") String parentId, @Param("angle") String angle);
+	
+}

+ 47 - 0
saas-account/src/main/java/com/persagy/account/mapper/SaasProjectProductMapper.java

@@ -0,0 +1,47 @@
+package com.persagy.account.mapper;
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.persagy.account.pojo.dto.SaasProjectProduct;
+
+/**
+ * 项目签约产品
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021-05-07 12:02:40
+ */
+@Mapper
+public interface SaasProjectProductMapper extends BaseMapper<SaasProjectProduct> {
+
+	/**
+	 * 查询产品项目签约信息,携带出项目名称
+	 * 
+	 * @param contractId
+	 * @param groupCode
+	 * @param projectId
+	 * @param productId
+	 * @return
+	 */
+	List<SaasProjectProduct> querySaasProjectProductList(@Param("contractId") String contractId, @Param("groupCode") String groupCode, 
+			@Param("projectId") String projectId, @Param("productId") String productId);
+
+	/**
+	 * 批量新增该产品集团下的签约项目
+	 * 
+	 * @param contractId
+	 * @param groupCode
+	 * @param productId
+	 * @param projectIds
+	 * @return
+	 */
+	Integer batchInsertSaasProjectProduct(@Param("contractId") String contractId, @Param("groupCode") String groupCode, 
+			@Param("productId") String productId, @Param("projectIds") Set<String> projectIds);
+	
+}

+ 14 - 0
saas-account/src/main/java/com/persagy/account/pojo/dto/SaasFunction.java

@@ -37,6 +37,9 @@ public class SaasFunction extends BaseEntity<SaasFunction> {
 
 
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	private String menuId; // 菜单ID
 	private String menuId; // 菜单ID
+	
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String pageId;	// 页面ID
 
 
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	private String funCode; // 功能编码
 	private String funCode; // 功能编码
@@ -102,6 +105,17 @@ public class SaasFunction extends BaseEntity<SaasFunction> {
 			}
 			}
 			return this;
 			return this;
 		}
 		}
+		
+		public Builder pageIdEq(String pageId) {
+			if (StringUtil.isNotBlank(pageId)) {
+				if (updateWrapper != null) {
+					updateWrapper.eq(SaasFunction::getPageId, pageId);
+				} else {
+					queryWrapper.eq(SaasFunction::getPageId, pageId);
+				}
+			}
+			return this;
+		}
 
 
 		public Builder funCodeEq(String funCode) {
 		public Builder funCodeEq(String funCode) {
 			if (StringUtil.isNotBlank(funCode)) {
 			if (StringUtil.isNotBlank(funCode)) {

+ 233 - 0
saas-account/src/main/java/com/persagy/account/pojo/dto/SaasGroupProduct.java

@@ -0,0 +1,233 @@
+package com.persagy.account.pojo.dto;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.persagy.common.annotation.IdGeneratorPrefix;
+import com.persagy.common.constant.SaasCommonConstant;
+import com.persagy.common.model.BaseEntity;
+import com.persagy.common.utils.StringUtil;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 集团签约产品
+ * 
+ * @version 1.0.0
+ * @company persagy
+ * @author zhangqiankun
+ * @date 2021-05-07 12:02:40
+ */
+@Getter
+@Setter
+@ToString
+@EqualsAndHashCode(callSuper = false)
+@TableName("saas_group_product")
+@IdGeneratorPrefix(SaasCommonConstant.SAAS_GROUP_PRODUCT_ID_PREFIX)
+public class SaasGroupProduct extends BaseEntity<SaasGroupProduct> {
+	private static final long serialVersionUID = -5143514069190729082L;
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String groupCode; // 集团编码
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String productId; // 产品ID
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String productCode; // 产品编码
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String productRange; // 产品使用范围,0-全部项目,1-部分项目
+
+	@TableField(fill = FieldFill.INSERT, updateStrategy = FieldStrategy.NOT_EMPTY)
+	private Date productContract; // 签约时间
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private Date validStartTime; // 签约有效期的开始时间
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private Date validEndTime; // 签约有效期的结束时间
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String validLast; // 签约有效期,0-期内有效,1-永久有效
+
+	@TableField(fill = FieldFill.INSERT_UPDATE, updateStrategy = FieldStrategy.NOT_EMPTY)
+	private Date updateTime; // 更新时间
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String updateUser; // 最后一次操作者ID
+	
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String remark; // 备注
+	
+	@TableField(exist = false)
+	private Integer projectNum; // 该产品下签约的项目数量
+
+	public static class BuilderQueryWrapper {
+
+		private LambdaQueryWrapper<SaasGroupProduct> queryWrapper = new LambdaQueryWrapper<SaasGroupProduct>();
+
+		public BuilderQueryWrapper idEq(String id) {
+			if (StringUtil.isNotBlank(id)) {
+				queryWrapper.eq(SaasGroupProduct::getId, id);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper groupCodeEq(String groupCode) {
+			if (StringUtil.isNotBlank(groupCode)) {
+				queryWrapper.eq(SaasGroupProduct::getGroupCode, groupCode);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper productIdEq(String productId) {
+			if (StringUtil.isNotBlank(productId)) {
+				queryWrapper.eq(SaasGroupProduct::getProductId, productId);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper productCodeEq(String productCode) {
+			if (StringUtil.isNotBlank(productCode)) {
+				queryWrapper.eq(SaasGroupProduct::getProductCode, productCode);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper productRangeEq(String productRange) {
+			if (StringUtil.isNotBlank(productRange)) {
+				queryWrapper.eq(SaasGroupProduct::getProductRange, productRange);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper productContractEq(Date productContract) {
+			if (null != productContract) {
+				queryWrapper.eq(SaasGroupProduct::getProductContract, productContract);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper validStartTimeEq(Date validStartTime) {
+			if (null != validStartTime) {
+				queryWrapper.eq(SaasGroupProduct::getValidStartTime, validStartTime);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper validEndTimeEq(Date validEndTime) {
+			if (null != validEndTime) {
+				queryWrapper.eq(SaasGroupProduct::getValidEndTime, validEndTime);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper validLastEq(String validLast) {
+			if (StringUtil.isNotBlank(validLast)) {
+				queryWrapper.eq(SaasGroupProduct::getValidLast, validLast);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper remarkEq(String remark) {
+			if (StringUtil.isNotBlank(remark)) {
+				queryWrapper.eq(SaasGroupProduct::getRemark, remark);
+			}
+			return this;
+		}
+
+		public LambdaQueryWrapper<SaasGroupProduct> builder() {
+			return queryWrapper;
+		}
+
+	}
+
+	public static class BuilderUpdateWrapper {
+
+		private LambdaUpdateWrapper<SaasGroupProduct> updateWrapper = new LambdaUpdateWrapper<SaasGroupProduct>();
+
+		public BuilderUpdateWrapper idEq(String id) {
+			if (StringUtil.isNotBlank(id)) {
+				updateWrapper.eq(SaasGroupProduct::getId, id);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper groupCodeEq(String groupCode) {
+			if (StringUtil.isNotBlank(groupCode)) {
+				updateWrapper.eq(SaasGroupProduct::getGroupCode, groupCode);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper productIdEq(String productId) {
+			if (StringUtil.isNotBlank(productId)) {
+				updateWrapper.eq(SaasGroupProduct::getProductId, productId);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper productCodeEq(String productCode) {
+			if (StringUtil.isNotBlank(productCode)) {
+				updateWrapper.eq(SaasGroupProduct::getProductCode, productCode);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper productRangeEq(String productRange) {
+			if (StringUtil.isNotBlank(productRange)) {
+				updateWrapper.eq(SaasGroupProduct::getProductRange, productRange);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper productContractEq(Date productContract) {
+			if (null != productContract) {
+				updateWrapper.eq(SaasGroupProduct::getProductContract, productContract);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper validStartTimeEq(Date validStartTime) {
+			if (null != validStartTime) {
+				updateWrapper.eq(SaasGroupProduct::getValidStartTime, validStartTime);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper validEndTimeEq(Date validEndTime) {
+			if (null != validEndTime) {
+				updateWrapper.eq(SaasGroupProduct::getValidEndTime, validEndTime);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper validLastEq(String validLast) {
+			if (StringUtil.isNotBlank(validLast)) {
+				updateWrapper.eq(SaasGroupProduct::getValidLast, validLast);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper remarkEq(String remark) {
+			if (StringUtil.isNotBlank(remark)) {
+				updateWrapper.eq(SaasGroupProduct::getRemark, remark);
+			}
+			return this;
+		}
+
+		public LambdaUpdateWrapper<SaasGroupProduct> builder() {
+			return updateWrapper;
+		}
+
+	}
+}

+ 85 - 0
saas-account/src/main/java/com/persagy/account/pojo/dto/SaasGroupProductDetail.java

@@ -0,0 +1,85 @@
+package com.persagy.account.pojo.dto;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.persagy.common.utils.StringUtil;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 集团签约产品详情
+ * 
+ * @version 1.0.0
+ * @company persagy
+ * @author zhangqiankun
+ * @date 2021-05-07 12:02:40
+ */
+@Getter
+@Setter
+@ToString
+@EqualsAndHashCode(callSuper = false)
+@TableName("saas_group_product_detail")
+public class SaasGroupProductDetail extends Model<SaasGroupProductDetail> {
+	private static final long serialVersionUID = -4851547634570454698L;
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String contractId; // 页面ID
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String pageId; // 页面ID
+
+	public static class BuilderQueryWrapper {
+
+		private LambdaQueryWrapper<SaasGroupProductDetail> queryWrapper = new LambdaQueryWrapper<SaasGroupProductDetail>();
+
+		public BuilderQueryWrapper contractIdEq(String contractId) {
+			if (StringUtil.isNotBlank(contractId)) {
+				queryWrapper.eq(SaasGroupProductDetail::getContractId, contractId);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper pageIdEq(String pageId) {
+			if (StringUtil.isNotBlank(pageId)) {
+				queryWrapper.eq(SaasGroupProductDetail::getPageId, pageId);
+			}
+			return this;
+		}
+
+		public LambdaQueryWrapper<SaasGroupProductDetail> builder() {
+			return queryWrapper;
+		}
+
+	}
+
+	public static class BuilderUpdateWrapper {
+
+		private LambdaUpdateWrapper<SaasGroupProductDetail> updateWrapper = new LambdaUpdateWrapper<SaasGroupProductDetail>();
+
+		public BuilderUpdateWrapper contractIdEq(String contractId) {
+			if (StringUtil.isNotBlank(contractId)) {
+				updateWrapper.eq(SaasGroupProductDetail::getContractId, contractId);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper pageIdEq(String pageId) {
+			if (StringUtil.isNotBlank(pageId)) {
+				updateWrapper.eq(SaasGroupProductDetail::getPageId, pageId);
+			}
+			return this;
+		}
+
+		public LambdaUpdateWrapper<SaasGroupProductDetail> builder() {
+			return updateWrapper;
+		}
+
+	}
+}

+ 22 - 5
saas-account/src/main/java/com/persagy/account/pojo/dto/SaasMenu.java

@@ -43,6 +43,9 @@ public class SaasMenu extends BaseEntity<SaasMenu> {
 	private String appId; // 应用ID, PC-电脑端,APP-手机端
 	private String appId; // 应用ID, PC-电脑端,APP-手机端
 
 
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String productId; // 产品ID
+	
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	private String menuCode; // 菜单编码
 	private String menuCode; // 菜单编码
 
 
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
@@ -61,7 +64,7 @@ public class SaasMenu extends BaseEntity<SaasMenu> {
 	private Integer menuSort; // 菜单顺序
 	private Integer menuSort; // 菜单顺序
 	
 	
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
-	private String menuType;	//菜单类型,0-系统菜单,1-标准业务菜单,2-IBMS菜单,3-APP菜单
+	private String menuType;	// 菜单类型,0-系统菜单,1-业务菜单
 
 
 	@TableField(fill = FieldFill.INSERT, updateStrategy = FieldStrategy.NOT_EMPTY)
 	@TableField(fill = FieldFill.INSERT, updateStrategy = FieldStrategy.NOT_EMPTY)
 	private Date createTime; // 创建时间
 	private Date createTime; // 创建时间
@@ -88,10 +91,13 @@ public class SaasMenu extends BaseEntity<SaasMenu> {
 	private String menuId;	//菜单ID
 	private String menuId;	//菜单ID
 	
 	
 	@TableField(exist = false)
 	@TableField(exist = false)
-	private List<SaasMenu> childrens;	//下级菜单
+	private List<SaasMenu> childrens;	 //下级菜单
+	
+	@TableField(exist = false)
+	private List<SaasProductPage> productPages; //此菜单下若有产品,代表顶级页面
 	
 	
 	@TableField(exist = false)
 	@TableField(exist = false)
-	private List<SaasFunction> functions;	//对应功能点集合
+	private List<SaasFunction> functions; //对应功能点集合
 	
 	
 	public static class Builder {
 	public static class Builder {
 
 
@@ -141,7 +147,18 @@ public class SaasMenu extends BaseEntity<SaasMenu> {
 			}
 			}
 			return this;
 			return this;
 		}
 		}
-
+		
+		public Builder productId(String productId) {
+			if (StringUtil.isNotBlank(productId)) {
+				if (updateWrapper != null) {
+					updateWrapper.eq(SaasMenu::getProductId, productId);
+				} else {
+					queryWrapper.eq(SaasMenu::getProductId, productId);
+				}
+			}
+			return this;
+		}
+		
 		public Builder menuCodeEq(String menuCode) {
 		public Builder menuCodeEq(String menuCode) {
 			if (StringUtil.isNotBlank(menuCode)) {
 			if (StringUtil.isNotBlank(menuCode)) {
 				if (updateWrapper != null) {
 				if (updateWrapper != null) {
@@ -218,7 +235,7 @@ public class SaasMenu extends BaseEntity<SaasMenu> {
 			}
 			}
 			return this;
 			return this;
 		}
 		}
-
+		
 		public Builder createTimeEq(Date createTime) {
 		public Builder createTimeEq(Date createTime) {
 			if (null != createTime) {
 			if (null != createTime) {
 				if (updateWrapper != null) {
 				if (updateWrapper != null) {

+ 21 - 0
saas-account/src/main/java/com/persagy/account/pojo/dto/SaasProduct.java

@@ -1,6 +1,7 @@
 package com.persagy.account.pojo.dto;
 package com.persagy.account.pojo.dto;
 
 
 import java.util.Date;
 import java.util.Date;
+import java.util.List;
 
 
 import com.baomidou.mybatisplus.annotation.FieldFill;
 import com.baomidou.mybatisplus.annotation.FieldFill;
 import com.baomidou.mybatisplus.annotation.FieldStrategy;
 import com.baomidou.mybatisplus.annotation.FieldStrategy;
@@ -36,6 +37,9 @@ public class SaasProduct extends BaseEntity<SaasProduct> {
 	private static final long serialVersionUID = -7374882065014346250L;
 	private static final long serialVersionUID = -7374882065014346250L;
 
 
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String appId; // 应用ID, PC-电脑端,APP-手机端
+	
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	private String productCode; // 产品编码
 	private String productCode; // 产品编码
 
 
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
@@ -61,6 +65,9 @@ public class SaasProduct extends BaseEntity<SaasProduct> {
 
 
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	private String remark; 		// 备注
 	private String remark; 		// 备注
+	
+	@TableField(exist = false)
+	private List<SaasProductPage> pages; 		// 下级所有页面集合
 
 
 	public static class BuilderQueryWrapper {
 	public static class BuilderQueryWrapper {
 
 
@@ -72,6 +79,13 @@ public class SaasProduct extends BaseEntity<SaasProduct> {
 			}
 			}
 			return this;
 			return this;
 		}
 		}
+		
+		public BuilderQueryWrapper appIdEq(String appId) {
+			if (StringUtil.isNotBlank(appId)) {
+				queryWrapper.eq(SaasProduct::getAppId, appId);
+			}
+			return this;
+		}
 
 
 		public BuilderQueryWrapper productCodeEq(String productCode) {
 		public BuilderQueryWrapper productCodeEq(String productCode) {
 			if (StringUtil.isNotBlank(productCode)) {
 			if (StringUtil.isNotBlank(productCode)) {
@@ -159,6 +173,13 @@ public class SaasProduct extends BaseEntity<SaasProduct> {
 			}
 			}
 			return this;
 			return this;
 		}
 		}
+		
+		public BuilderUpdateWrapper appIdEq(String appId) {
+			if (StringUtil.isNotBlank(appId)) {
+				updateWrapper.eq(SaasProduct::getAppId, appId);
+			}
+			return this;
+		}
 
 
 		public BuilderUpdateWrapper productCodeEq(String productCode) {
 		public BuilderUpdateWrapper productCodeEq(String productCode) {
 			if (StringUtil.isNotBlank(productCode)) {
 			if (StringUtil.isNotBlank(productCode)) {

+ 291 - 0
saas-account/src/main/java/com/persagy/account/pojo/dto/SaasProductPage.java

@@ -0,0 +1,291 @@
+package com.persagy.account.pojo.dto;
+
+import java.util.Date;
+import java.util.List;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.google.common.collect.Sets;
+import com.persagy.common.annotation.IdGeneratorPrefix;
+import com.persagy.common.constant.SaasCommonConstant;
+import com.persagy.common.model.BaseEntity;
+import com.persagy.common.utils.StringUtil;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 产品页面配置
+ * 
+ * @version 1.0.0
+ * @company persagy
+ * @author zhangqiankun
+ * @date 2021-05-06 10:12:47
+ */
+@Getter
+@Setter
+@ToString
+@EqualsAndHashCode(callSuper = false)
+@TableName("saas_product_page")
+@IdGeneratorPrefix(SaasCommonConstant.SAAS_PRODUCT_PAGE_ID_PREFIX)
+public class SaasProductPage extends BaseEntity<SaasProductPage> {
+	private static final long serialVersionUID = 448704137255167187L;
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String productId; // 产品ID
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String parentId; // 上级页面ID
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String pageName; // 页面名称
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String pageIcon; // 页面图标
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String pageUrl; // 页面URL,一般为前端页面跳转链接
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String pageAngle; // 页面视角,0-集团项目页,1-集团独有页,2-项目独有页,默认0
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String pageFloor; // 是否为落地页,0-非落地页,1-集团落地页,2-项目落地页,3-集团项目落地页
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private Integer pageSort;	// 页面顺序
+	
+	@TableField(fill = FieldFill.INSERT, updateStrategy = FieldStrategy.NOT_EMPTY)
+	private Date createTime; // 创建时间
+
+	@TableField(fill = FieldFill.INSERT_UPDATE, updateStrategy = FieldStrategy.NOT_EMPTY)
+	private Date updateTime; // 更新时间
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String updateUser; // 最后一次操作者ID
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String remark; // 备注
+	
+	@TableField(exist = false)
+	private List<SaasProductPage> childrens; 	// 下级所有页面集合
+	
+	@TableField(exist = false)
+	private List<SaasFunction> functions; 		// 本页面对应的权限集合
+
+	public static class BuilderQueryWrapper {
+
+		private LambdaQueryWrapper<SaasProductPage> queryWrapper = new LambdaQueryWrapper<SaasProductPage>();
+
+		public BuilderQueryWrapper idEq(String id) {
+			if (StringUtil.isNotBlank(id)) {
+				queryWrapper.eq(SaasProductPage::getId, id);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper productIdEq(String productId) {
+			if (StringUtil.isNotBlank(productId)) {
+				queryWrapper.eq(SaasProductPage::getProductId, productId);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper parentIdEq(String parentId) {
+			if (StringUtil.isNotBlank(parentId)) {
+				queryWrapper.eq(SaasProductPage::getParentId, parentId);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper pageNameEq(String pageName) {
+			if (StringUtil.isNotBlank(pageName)) {
+				queryWrapper.eq(SaasProductPage::getPageName, pageName);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper pageIconEq(String pageIcon) {
+			if (StringUtil.isNotBlank(pageIcon)) {
+				queryWrapper.eq(SaasProductPage::getPageIcon, pageIcon);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper pageUrlEq(String pageUrl) {
+			if (StringUtil.isNotBlank(pageUrl)) {
+				queryWrapper.eq(SaasProductPage::getPageUrl, pageUrl);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper pageAngleEq(String pageAngle) {
+			if (StringUtil.isNotBlank(pageAngle)) {
+				queryWrapper.eq(SaasProductPage::getPageAngle, pageAngle);
+			}
+			return this;
+		}
+		
+		public BuilderQueryWrapper pageAngleIn(String angle) {
+			if (StringUtil.isNotBlank(angle)) {
+				if (SaasCommonConstant.STR_STATUS_1.equals(angle)) {
+					queryWrapper.in(SaasProductPage::getPageAngle, Sets.newHashSet(SaasCommonConstant.STR_STATUS_0, SaasCommonConstant.STR_STATUS_1));
+				} else {
+					queryWrapper.in(SaasProductPage::getPageAngle, Sets.newHashSet(SaasCommonConstant.STR_STATUS_0, SaasCommonConstant.STR_STATUS_2));
+				}
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper pageFloorEq(String pageFloor) {
+			if (StringUtil.isNotBlank(pageFloor)) {
+				queryWrapper.eq(SaasProductPage::getPageFloor, pageFloor);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper createTimeEq(Date createTime) {
+			if (null != createTime) {
+				queryWrapper.eq(SaasProductPage::getCreateTime, createTime);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper updateTimeEq(Date updateTime) {
+			if (null != updateTime) {
+				queryWrapper.eq(SaasProductPage::getUpdateTime, updateTime);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper updateUserEq(String updateUser) {
+			if (StringUtil.isNotBlank(updateUser)) {
+				queryWrapper.eq(SaasProductPage::getUpdateUser, updateUser);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper remarkEq(String remark) {
+			if (StringUtil.isNotBlank(remark)) {
+				queryWrapper.eq(SaasProductPage::getRemark, remark);
+			}
+			return this;
+		}
+
+		public LambdaQueryWrapper<SaasProductPage> builder() {
+			return queryWrapper;
+		}
+
+	}
+
+	public static class BuilderUpdateWrapper {
+
+		private LambdaUpdateWrapper<SaasProductPage> updateWrapper = new LambdaUpdateWrapper<SaasProductPage>();
+
+		public BuilderUpdateWrapper idEq(String id) {
+			if (StringUtil.isNotBlank(id)) {
+				updateWrapper.eq(SaasProductPage::getId, id);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper productIdEq(String productId) {
+			if (StringUtil.isNotBlank(productId)) {
+				updateWrapper.eq(SaasProductPage::getProductId, productId);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper parentIdEq(String parentId) {
+			if (StringUtil.isNotBlank(parentId)) {
+				updateWrapper.eq(SaasProductPage::getParentId, parentId);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper pageNameEq(String pageName) {
+			if (StringUtil.isNotBlank(pageName)) {
+				updateWrapper.eq(SaasProductPage::getPageName, pageName);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper pageIconEq(String pageIcon) {
+			if (StringUtil.isNotBlank(pageIcon)) {
+				updateWrapper.eq(SaasProductPage::getPageIcon, pageIcon);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper pageUrlEq(String pageUrl) {
+			if (StringUtil.isNotBlank(pageUrl)) {
+				updateWrapper.eq(SaasProductPage::getPageUrl, pageUrl);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper pageAngleEq(String pageAngle) {
+			if (StringUtil.isNotBlank(pageAngle)) {
+				updateWrapper.eq(SaasProductPage::getPageAngle, pageAngle);
+			}
+			return this;
+		}
+		
+		public BuilderUpdateWrapper pageAngleIn(String angle) {
+			if (StringUtil.isNotBlank(angle)) {
+				if (SaasCommonConstant.STR_STATUS_1.equals(angle)) {
+					updateWrapper.in(SaasProductPage::getPageAngle, Sets.newHashSet(SaasCommonConstant.STR_STATUS_0, SaasCommonConstant.STR_STATUS_1));
+				} else {
+					updateWrapper.in(SaasProductPage::getPageAngle, Sets.newHashSet(SaasCommonConstant.STR_STATUS_0, SaasCommonConstant.STR_STATUS_2));
+				}
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper pageFloorEq(String pageFloor) {
+			if (StringUtil.isNotBlank(pageFloor)) {
+				updateWrapper.eq(SaasProductPage::getPageFloor, pageFloor);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper createTimeEq(Date createTime) {
+			if (null != createTime) {
+				updateWrapper.eq(SaasProductPage::getCreateTime, createTime);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper updateTimeEq(Date updateTime) {
+			if (null != updateTime) {
+				updateWrapper.eq(SaasProductPage::getUpdateTime, updateTime);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper updateUserEq(String updateUser) {
+			if (StringUtil.isNotBlank(updateUser)) {
+				updateWrapper.eq(SaasProductPage::getUpdateUser, updateUser);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper remarkEq(String remark) {
+			if (StringUtil.isNotBlank(remark)) {
+				updateWrapper.eq(SaasProductPage::getRemark, remark);
+			}
+			return this;
+		}
+
+		public LambdaUpdateWrapper<SaasProductPage> builder() {
+			return updateWrapper;
+		}
+
+	}
+}

+ 159 - 0
saas-account/src/main/java/com/persagy/account/pojo/dto/SaasProjectProduct.java

@@ -0,0 +1,159 @@
+package com.persagy.account.pojo.dto;
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.persagy.common.utils.StringUtil;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 项目签约产品
+ * 
+ * @version 1.0.0
+ * @company persagy
+ * @author zhangqiankun
+ * @date 2021-05-07 12:02:40
+ */
+@Getter
+@Setter
+@ToString
+@EqualsAndHashCode(callSuper = false)
+@TableName("saas_project_product")
+public class SaasProjectProduct extends Model<SaasProjectProduct> {
+	private static final long serialVersionUID = 4396612247398410758L;
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String contractId; // 集团签约ID
+	
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String groupCode; // 集团编码
+	
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String projectId; // 产品ID
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String productId; // 产品ID
+
+	@TableField(fill = FieldFill.INSERT, updateStrategy = FieldStrategy.NOT_EMPTY)
+	private Date productContract; // 签约时间
+
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String remark; // 备注
+	
+	@TableField(exist = false)
+	private String projectName; // 项目名称
+
+	public static class BuilderQueryWrapper {
+
+		private LambdaQueryWrapper<SaasProjectProduct> queryWrapper = new LambdaQueryWrapper<SaasProjectProduct>();
+
+		public BuilderQueryWrapper contractIdEq(String contractId) {
+			if (StringUtil.isNotBlank(contractId)) {
+				queryWrapper.eq(SaasProjectProduct::getContractId, contractId);
+			}
+			return this;
+		}
+		
+		public BuilderQueryWrapper groupCodeEq(String groupCode) {
+			if (StringUtil.isNotBlank(groupCode)) {
+				queryWrapper.eq(SaasProjectProduct::getGroupCode, groupCode);
+			}
+			return this;
+		}
+		
+		public BuilderQueryWrapper projectIdEq(String projectId) {
+			if (StringUtil.isNotBlank(projectId)) {
+				queryWrapper.eq(SaasProjectProduct::getProjectId, projectId);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper productIdEq(String productId) {
+			if (StringUtil.isNotBlank(productId)) {
+				queryWrapper.eq(SaasProjectProduct::getProductId, productId);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper productContractEq(Date productContract) {
+			if (null != productContract) {
+				queryWrapper.eq(SaasProjectProduct::getProductContract, productContract);
+			}
+			return this;
+		}
+
+		public BuilderQueryWrapper remarkEq(String remark) {
+			if (StringUtil.isNotBlank(remark)) {
+				queryWrapper.eq(SaasProjectProduct::getRemark, remark);
+			}
+			return this;
+		}
+
+		public LambdaQueryWrapper<SaasProjectProduct> builder() {
+			return queryWrapper;
+		}
+
+	}
+
+	public static class BuilderUpdateWrapper {
+
+		private LambdaUpdateWrapper<SaasProjectProduct> updateWrapper = new LambdaUpdateWrapper<SaasProjectProduct>();
+
+		public BuilderUpdateWrapper contractIdEq(String contractId) {
+			if (StringUtil.isNotBlank(contractId)) {
+				updateWrapper.eq(SaasProjectProduct::getContractId, contractId);
+			}
+			return this;
+		}
+		
+		public BuilderUpdateWrapper groupCodeEq(String groupCode) {
+			if (StringUtil.isNotBlank(groupCode)) {
+				updateWrapper.eq(SaasProjectProduct::getGroupCode, groupCode);
+			}
+			return this;
+		}
+		
+		public BuilderUpdateWrapper projectIdEq(String projectId) {
+			if (StringUtil.isNotBlank(projectId)) {
+				updateWrapper.eq(SaasProjectProduct::getProjectId, projectId);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper productIdEq(String productId) {
+			if (StringUtil.isNotBlank(productId)) {
+				updateWrapper.eq(SaasProjectProduct::getProductId, productId);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper productContractEq(Date productContract) {
+			if (null != productContract) {
+				updateWrapper.eq(SaasProjectProduct::getProductContract, productContract);
+			}
+			return this;
+		}
+
+		public BuilderUpdateWrapper remarkEq(String remark) {
+			if (StringUtil.isNotBlank(remark)) {
+				updateWrapper.eq(SaasProjectProduct::getRemark, remark);
+			}
+			return this;
+		}
+
+		public LambdaUpdateWrapper<SaasProjectProduct> builder() {
+			return updateWrapper;
+		}
+
+	}
+}

+ 14 - 0
saas-account/src/main/java/com/persagy/account/pojo/dto/SaasRoleMenu.java

@@ -37,6 +37,9 @@ public class SaasRoleMenu extends Model<SaasRoleMenu> {
 
 
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	private String menuId; 	// 菜单ID
 	private String menuId; 	// 菜单ID
+	
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String pageId; 	// 页面ID
 
 
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	private String functionId; // 功能ID
 	private String functionId; // 功能ID
@@ -89,6 +92,17 @@ public class SaasRoleMenu extends Model<SaasRoleMenu> {
 			}
 			}
 			return this;
 			return this;
 		}
 		}
+		
+		public Builder pageIdEq(String pageId) {
+			if (StringUtil.isNotBlank(pageId)) {
+				if (updateWrapper != null) {
+					updateWrapper.eq(SaasRoleMenu::getPageId, pageId);
+				} else {
+					queryWrapper.eq(SaasRoleMenu::getPageId, pageId);
+				}
+			}
+			return this;
+		}
 
 
 		public Builder functionIdEq(String functionId) {
 		public Builder functionIdEq(String functionId) {
 			if (StringUtil.isNotBlank(functionId)) {
 			if (StringUtil.isNotBlank(functionId)) {

+ 3 - 0
saas-account/src/main/java/com/persagy/account/pojo/vo/account/SaasAccountQueryVO.java

@@ -38,6 +38,9 @@ public class SaasAccountQueryVO {
 	@ApiModelProperty(value = "角色ID", example = "RY56122123152")
 	@ApiModelProperty(value = "角色ID", example = "RY56122123152")
 	private String roleId; 	// 角色ID
 	private String roleId; 	// 角色ID
 	
 	
+	@ApiModelProperty(value = "角色ID集合", example = "RY56122123152")
+	private Set<String> roleIds; 	// 角色ID集合
+	
 	@ApiModelProperty(value = "账号类型, 0-超级管理员(所有集团),1-单集团管理员(单集团),2-普通账号", example = "0")
 	@ApiModelProperty(value = "账号类型, 0-超级管理员(所有集团),1-单集团管理员(单集团),2-普通账号", example = "0")
 	private String accountType;		//账号类型, 0-超级管理员(所有集团),1-单集团管理员(单集团),2-普通账号
 	private String accountType;		//账号类型, 0-超级管理员(所有集团),1-单集团管理员(单集团),2-普通账号
 	
 	

+ 6 - 3
saas-account/src/main/java/com/persagy/account/pojo/vo/function/SaasFunctionCreateVO.java

@@ -28,11 +28,14 @@ public class SaasFunctionCreateVO extends BaseUserModel {
 	@ApiModelProperty(value = "功能ID,更新时不可为空", example = "123")
 	@ApiModelProperty(value = "功能ID,更新时不可为空", example = "123")
 	private String id; // 功能ID
 	private String id; // 功能ID
 	
 	
-	@NotBlank(message = "菜单ID,不可为空")
+	//@NotBlank(message = "菜单ID,不可为空")
 	@ApiModelProperty(value = "菜单ID", example = "123")
 	@ApiModelProperty(value = "菜单ID", example = "123")
 	private String menuId; // 菜单ID
 	private String menuId; // 菜单ID
+	
+	@ApiModelProperty(value = "页面ID", example = "123")
+	private String pageId;	// 页面ID
 
 
-	@NotBlank(message = "功能编码,不可为空")
+	//@NotBlank(message = "功能编码,不可为空")
 	@ApiModelProperty(value = "功能编码", example = "456")
 	@ApiModelProperty(value = "功能编码", example = "456")
 	private String funCode; // 功能编码
 	private String funCode; // 功能编码
 
 
@@ -40,7 +43,7 @@ public class SaasFunctionCreateVO extends BaseUserModel {
 	@ApiModelProperty(value = "功能名称", example = "测试功能名称")
 	@ApiModelProperty(value = "功能名称", example = "测试功能名称")
 	private String funName; // 功能名称
 	private String funName; // 功能名称
 	
 	
-	@NotBlank(message = "功能URL,不可为空")
+	//@NotBlank(message = "功能URL,不可为空")
 	@ApiModelProperty(value = "功能URL", example = "http://127.0.0.1:8080/test.png")
 	@ApiModelProperty(value = "功能URL", example = "http://127.0.0.1:8080/test.png")
 	private String funUrl;	// 功能URL
 	private String funUrl;	// 功能URL
 
 

+ 0 - 3
saas-account/src/main/java/com/persagy/account/pojo/vo/function/SaasFunctionDeleteVO.java

@@ -25,7 +25,4 @@ public class SaasFunctionDeleteVO extends BaseUserModel {
 	@ApiModelProperty(value = "功能ID", example = "123456")
 	@ApiModelProperty(value = "功能ID", example = "123456")
 	private String id; 		// 功能ID
 	private String id; 		// 功能ID
 	
 	
-	@ApiModelProperty(value = "菜单ID", example = "123456")
-	private String menuId; 	// 菜单ID
-	
 }
 }

+ 4 - 1
saas-account/src/main/java/com/persagy/account/pojo/vo/function/SaasFunctionQueryVO.java

@@ -30,7 +30,10 @@ public class SaasFunctionQueryVO extends BaseUserModel {
 	
 	
 	@ApiModelProperty(value = "菜单ID", example = "123")
 	@ApiModelProperty(value = "菜单ID", example = "123")
 	private String menuId; // 菜单ID
 	private String menuId; // 菜单ID
-
+	
+	@ApiModelProperty(value = "页面ID", example = "123")
+	private String pageId; // 页面ID
+	
 	@ApiModelProperty(value = "功能编码", example = "456")
 	@ApiModelProperty(value = "功能编码", example = "456")
 	private String funCode; // 功能编码
 	private String funCode; // 功能编码
 
 

+ 47 - 0
saas-account/src/main/java/com/persagy/account/pojo/vo/function/SaasMenuFunctionVO.java

@@ -0,0 +1,47 @@
+package com.persagy.account.pojo.vo.function;
+
+import javax.validation.constraints.NotBlank;
+
+import com.persagy.common.model.BaseUserModel;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 菜单信息
+ * 
+ * @version 1.0.0
+ * @company persagy
+ * @author zhangqiankun
+ * @date 2021-03-13 15:29:50
+ */
+@Getter
+@Setter
+@ToString
+@ApiModel
+public class SaasMenuFunctionVO extends BaseUserModel {
+
+	@ApiModelProperty(value = "集团编码,作为SQL条件", example = "RC")
+	private String groupCode; 	// 所属集团编码
+	
+	@ApiModelProperty(value = "项目ID", example = "Pj123456789")
+	private String projectId; 	// 项目ID
+	
+	@NotBlank(message = "菜单ID,不可为空")
+	@ApiModelProperty(value = "菜单ID", example = "123")
+	private String menuId; // 菜单ID
+	
+	@NotBlank(message = "当前角度,不可为空")
+	@ApiModelProperty(value = "当前角度", example = "123")
+	private String angle; // 当前角度,1-集团视角,2-项目视角
+	
+	@ApiModelProperty(value = "产品ID", example = "PD1001")
+	private String productId; // 产品ID
+
+	@ApiModelProperty(value = "页面ID", example = "123")
+	private String pageId; // 页面ID
+
+}

+ 6 - 3
saas-account/src/main/java/com/persagy/account/pojo/vo/menu/SaasMenuCreateVO.java

@@ -32,7 +32,10 @@ public class SaasMenuCreateVO extends BaseUserModel {
 	@ApiModelProperty(value = "上级菜单ID", example = "123456")
 	@ApiModelProperty(value = "上级菜单ID", example = "123456")
 	private String parentId; // 上级菜单ID
 	private String parentId; // 上级菜单ID
 
 
-	@NotBlank(message = "菜单编码,不可为空")
+	@ApiModelProperty(value = "产品ID", example = "PD1001")
+	private String productId; // 产品ID
+	
+	//@NotBlank(message = "菜单编码,不可为空")
 	@ApiModelProperty(value = "菜单编码", example = "ceshi", required = true)
 	@ApiModelProperty(value = "菜单编码", example = "ceshi", required = true)
 	private String menuCode; // 菜单编码
 	private String menuCode; // 菜单编码
 	
 	
@@ -56,8 +59,8 @@ public class SaasMenuCreateVO extends BaseUserModel {
 	private Integer menuSort; // 菜单顺序
 	private Integer menuSort; // 菜单顺序
 
 
 	@NotBlank(message = "菜单类型,不可为空")
 	@NotBlank(message = "菜单类型,不可为空")
-	@ApiModelProperty(value = "菜单类型,0-系统菜单,1-标准业务菜单,2-IBMS菜单,3-APP菜单", example = "0")
-	private String menuType;	//菜单类型,0-系统菜单,1-标准业务菜单,2-IBMS菜单,3-APP菜单
+	@ApiModelProperty(value = "菜单类型,0-系统菜单,1-业务菜单", example = "0")
+	private String menuType;	//菜单类型,0-系统菜单,1-业务菜单
 	
 	
 	@ApiModelProperty(value = "备注", example = "测试菜单")
 	@ApiModelProperty(value = "备注", example = "测试菜单")
 	private String remark;   // 备注
 	private String remark;   // 备注

+ 8 - 2
saas-account/src/main/java/com/persagy/account/pojo/vo/menu/SaasMenuQueryVO.java

@@ -28,8 +28,14 @@ public class SaasMenuQueryVO extends BaseUserModel {
 	@ApiModelProperty(value = "菜单ID", example = "ME123456789")
 	@ApiModelProperty(value = "菜单ID", example = "ME123456789")
 	private String menuId; 		// 菜单ID
 	private String menuId; 		// 菜单ID
 	
 	
-	@ApiModelProperty(value = "菜单类型,0-系统菜单,1-标准业务菜单,2-IBMS菜单,3-APP菜单", example = "0")
-	private String menuType; 		// 菜单类型,0-系统菜单,1-标准业务菜单,2-IBMS菜单,3-APP菜单
+	@ApiModelProperty(value = "项目ID", example = "Pj123456789")
+	private String projectId; 	// 项目ID,存在即为项目视角
+	
+	@ApiModelProperty(value = "产品ID", example = "PD123456789")
+	private String productId; 		// 产品ID
+	
+	@ApiModelProperty(value = "菜单类型,0-系统菜单,1-业务菜单", example = "0")
+	private String menuType; 		// 菜单类型,0-系统菜单,1-业务菜单
 	
 	
 	@ApiModelProperty(value = "是否仅查询出菜单树,true-是,默认false", example = "false")
 	@ApiModelProperty(value = "是否仅查询出菜单树,true-是,默认false", example = "false")
 	private boolean isMenu = false; 	// 是否仅查询出菜单树,true-是,默认false
 	private boolean isMenu = false; 	// 是否仅查询出菜单树,true-是,默认false

+ 32 - 0
saas-account/src/main/java/com/persagy/account/pojo/vo/menu/SaasMenuSort.java

@@ -0,0 +1,32 @@
+package com.persagy.account.pojo.vo.menu;
+
+import javax.validation.constraints.NotBlank;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021年5月6日 下午2:38:04
+ */
+@Getter
+@Setter
+@ApiModel
+@ToString
+public class SaasMenuSort {
+	
+	@NotBlank(message = "菜单ID,不可为空")
+	@ApiModelProperty(value = "菜单ID", example = "123456", required = true)
+	private String menuId;
+	
+	@NotBlank(message = "菜单顺序,不可为空")
+	@ApiModelProperty(value = "菜单顺序", example = "1", required = true)
+	private Integer menuSort;
+	
+}

+ 32 - 0
saas-account/src/main/java/com/persagy/account/pojo/vo/menu/SaasMenuSortVO.java

@@ -0,0 +1,32 @@
+package com.persagy.account.pojo.vo.menu;
+
+import java.util.List;
+
+import javax.validation.constraints.NotEmpty;
+
+import com.persagy.common.model.BaseUserModel;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021年5月6日 下午2:36:34
+ */
+@Getter
+@Setter
+@ApiModel
+@ToString
+public class SaasMenuSortVO extends BaseUserModel {
+	
+	@NotEmpty(message = "菜单集合不可为空")
+	@ApiModelProperty(value = "菜单集合", required = true)
+	private List<SaasMenuSort> menus;
+	
+}

+ 63 - 0
saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasGroupProductCreateVO.java

@@ -0,0 +1,63 @@
+package com.persagy.account.pojo.vo.product;
+
+import java.util.Date;
+import java.util.Set;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotEmpty;
+
+import com.persagy.common.model.BaseGroupModel;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 产品签约信息
+ * 
+ * @version 1.0.0
+ * @company persagy
+ * @author zhangqiankun
+ * @date 2021-05-07 10:31:47
+ */
+@Getter
+@Setter
+@ApiModel
+@ToString
+public class SaasGroupProductCreateVO extends BaseGroupModel {
+	
+	@ApiModelProperty(value = "签约ID,更新时不可为空", example = "1001")
+	private String id;
+	
+	@NotBlank(message = "产品ID,不可为空")
+	@ApiModelProperty(value = "产品ID", example = "1001", required = true)
+	private String productId; // 产品ID
+
+	@NotBlank(message = "产品编码,不可为空")
+	@ApiModelProperty(value = "产品编码", example = "0", required = true)
+	private String productCode; // 产品编码
+
+	@NotBlank(message = "产品使用范围,不可为空")
+	@ApiModelProperty(value = "产品使用范围,0-全部项目,1-部分项目", example = "0", required = true)
+	private String productRange; // 产品使用范围,0-全部项目,1-部分项目
+	
+	@NotEmpty(message = "页面ID集合,不可为空")
+	@ApiModelProperty(value = "页面ID集合", example = "PA1001")
+	private Set<String> pageIds; // 页面ID集合
+	
+	@ApiModelProperty(value = "签约有效期的开始时间", example = "6456465165")
+	private Date validStartTime; // 签约有效期的开始时间
+
+	@ApiModelProperty(value = "签约有效期的结束时间", example = "6456465165")
+	private Date validEndTime; // 签约有效期的结束时间
+
+	@NotBlank(message = "签约有效期,不可为空")
+	@ApiModelProperty(value = "签约有效期,0-期内有效,1-永久有效", example = "0")
+	private String validLast; // 签约有效期,0-期内有效,1-永久有效
+	
+	@ApiModelProperty(value = "备注", example = "测试")
+	private String remark; // 备注
+
+}

+ 31 - 0
saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasGroupProductDeleteVO.java

@@ -0,0 +1,31 @@
+package com.persagy.account.pojo.vo.product;
+
+import javax.validation.constraints.NotEmpty;
+
+import com.persagy.common.model.BaseGroupModel;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 产品签约信息
+ * 
+ * @version 1.0.0
+ * @company persagy
+ * @author zhangqiankun
+ * @date 2021-05-07 10:31:47
+ */
+@Getter
+@Setter
+@ApiModel
+@ToString
+public class SaasGroupProductDeleteVO extends BaseGroupModel {
+	
+	@NotEmpty(message = "产品ID,不可为空")
+	@ApiModelProperty(value = "产品ID", example = "1001")
+	private String productId; // 产品ID
+	
+}

+ 36 - 0
saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasGroupProductPageVO.java

@@ -0,0 +1,36 @@
+package com.persagy.account.pojo.vo.product;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 项目信息
+ * 
+ * @version 1.0.0
+ * @company persagy
+ * @author zhangqiankun
+ * @date 2021-03-13 15:29:50
+ */
+@Getter
+@Setter
+@ApiModel
+@ToString
+public class SaasGroupProductPageVO extends SaasGroupProductQueryVO {
+
+	@Min(value = 1, message = "分页页码,不可低于1")
+	@NotNull(message = "分页页码,不可为空")
+	@ApiModelProperty(value = "分页页码", example = "1", required = true)
+	private Integer page;
+
+	@Min(value = 1, message = "分页大小,不可低于1")
+	@NotNull(message = "分页大小,不可为空")
+	@ApiModelProperty(value = "分页大小", example = "10", required = true)
+	private Integer size;
+
+}

+ 34 - 0
saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasGroupProductQueryVO.java

@@ -0,0 +1,34 @@
+package com.persagy.account.pojo.vo.product;
+
+import com.persagy.account.pojo.vo.BaseGroupVO;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 产品签约信息
+ * 
+ * @version 1.0.0
+ * @company persagy
+ * @author zhangqiankun
+ * @date 2021-05-07 10:31:47
+ */
+@Getter
+@Setter
+@ApiModel
+@ToString
+public class SaasGroupProductQueryVO extends BaseGroupVO {
+	
+	@ApiModelProperty(value = "产品ID", example = "1001")
+	private String productId; // 产品ID
+	
+	@ApiModelProperty(value = "签约有效期,0-期内有效,1-永久有效", example = "0")
+	private String validLast; // 签约有效期,0-期内有效,1-永久有效
+	
+	@ApiModelProperty(value = "是否统计项目数,true-统计,默认false", example = "false")
+	private boolean totalProject = false;	//是否统计项目数,true-统计,默认false
+
+}

+ 62 - 0
saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasProductPageCreateVO.java

@@ -0,0 +1,62 @@
+package com.persagy.account.pojo.vo.product;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+import com.persagy.common.constant.SaasCommonConstant;
+import com.persagy.common.model.BaseUserModel;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021年5月6日 上午10:21:23
+ */
+@Getter
+@Setter
+@ApiModel
+@ToString
+public class SaasProductPageCreateVO extends BaseUserModel {
+	
+	@ApiModelProperty(value = "页面ID,更新时不可为空", example = "PD1001")
+	private String id; // 页面ID
+	
+	@NotBlank(message = "产品ID,不可为空")
+	@ApiModelProperty(value = "产品ID", example = "PD1001", required = true)
+	private String productId; // 产品ID
+
+	@ApiModelProperty(value = "上级页面ID", example = "1001")
+	private String parentId; // 上级页面ID
+
+	@NotBlank(message = "页面名称,不可为空")
+	@ApiModelProperty(value = "页面名称", example = "1001", required = true)
+	private String pageName; // 页面名称
+	
+	@ApiModelProperty(value = "页面图标", example = "1001")
+	private String pageIcon; // 页面图标
+
+	@ApiModelProperty(value = "页面URL", example = "1001")
+	private String pageUrl; // 页面URL,一般为前端页面跳转链接
+
+	@NotBlank(message = "页面视角,不可为空")
+	@ApiModelProperty(value = "页面视角,0-集团项目页,1-集团独有页,2-项目独有页", example = "1001", required = true)
+	private String pageAngle; // 页面视角,0-集团项目页,1-集团独有页,2-项目独有页
+
+	@ApiModelProperty(value = "是否为落地页,0-非落地页,1-集团落地页,2-项目落地页,3-集团项目落地页,默认0", example = "1001", required = true)
+	private String pageFloor = SaasCommonConstant.STR_STATUS_0; // 是否为落地页,0-非落地页,1-集团落地页,2-项目落地页,3-集团项目落地页
+	
+	@NotNull(message = "页面顺序,不可为空")
+	@ApiModelProperty(value = "页面顺序", example = "1", required = true)
+	private Integer pageSort;	// 页面顺序
+	
+	@ApiModelProperty(value = "备注", example = "1001")
+	private String remark; // 备注
+	
+}

+ 30 - 0
saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasProductPageDeleteVO.java

@@ -0,0 +1,30 @@
+package com.persagy.account.pojo.vo.product;
+
+import javax.validation.constraints.NotBlank;
+
+import com.persagy.common.model.BaseUserModel;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021年5月6日 上午11:26:41
+ */
+@Getter
+@Setter
+@ApiModel
+@ToString
+public class SaasProductPageDeleteVO extends BaseUserModel {
+	
+	@NotBlank(message = "页面ID,不可为空")
+	@ApiModelProperty(value = "页面ID", example = "PD1001", required = true)
+	private String id; // 页面ID
+	
+}

+ 36 - 0
saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasProductPageTreeVO.java

@@ -0,0 +1,36 @@
+package com.persagy.account.pojo.vo.product;
+
+import javax.validation.constraints.NotBlank;
+
+import com.persagy.common.model.BaseUserModel;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021年5月6日 上午10:21:23
+ */
+@Getter
+@Setter
+@ApiModel
+@ToString
+public class SaasProductPageTreeVO extends BaseUserModel {
+	
+	@NotBlank(message = "产品ID,不可为空")
+	@ApiModelProperty(value = "产品ID", example = "PD1001")
+	private String productId; // 产品ID
+	
+	@ApiModelProperty(value = "当前角度", example = "123")
+	private String angle; // 当前角度,1-集团视角,2-项目视角
+
+	@ApiModelProperty(value = "是否展示出页面对应的权限信息,true-展示,默认false", example = "true")
+	private boolean showPageAuth = false; // 是否展示出页面对应的权限信息,true-展示,默认false
+	
+}

+ 41 - 0
saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasProjectProductBatchVO.java

@@ -0,0 +1,41 @@
+package com.persagy.account.pojo.vo.product;
+
+import java.util.Set;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotEmpty;
+
+import com.persagy.account.pojo.vo.BaseGroupVO;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021年5月7日 下午2:07:15
+ */
+@Getter
+@Setter
+@ApiModel
+@ToString
+public class SaasProjectProductBatchVO extends BaseGroupVO {
+
+	@NotBlank(message = "集团签约ID,不可为空")
+	@ApiModelProperty(value = "集团签约ID", example = "GP1001", required = true)
+	private String contractId; //集团签约ID
+	
+	@NotBlank(message = "产品ID,不可为空")
+	@ApiModelProperty(value = "产品ID", example = "PD1001", required = true)
+	private String productId; // 产品ID
+	
+	@NotEmpty(message = "项目ID集合,不可为空")
+	@ApiModelProperty(value = "项目ID集合", example = "Pj1001", required = true)
+	private Set<String> projectIds; // 项目ID集合
+
+}

+ 37 - 0
saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasProjectProductCreateVO.java

@@ -0,0 +1,37 @@
+package com.persagy.account.pojo.vo.product;
+
+import javax.validation.constraints.NotBlank;
+
+import com.persagy.common.model.BaseGroupModel;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021年5月7日 下午2:26:09
+ */
+@Getter
+@Setter
+@ApiModel
+@ToString
+public class SaasProjectProductCreateVO extends BaseGroupModel {
+	
+	@NotBlank(message = "项目ID,不可为空")
+	@ApiModelProperty(value = "项目ID", example = "Pj1001")
+	private String projectId; // 项目ID
+
+	@NotBlank(message = "产品ID,不可为空")
+	@ApiModelProperty(value = "产品ID", example = "PD1001")
+	private String productId; // 产品ID
+	
+	@ApiModelProperty(value = "备注", example = "测试")
+	private String remark; // 备注
+	
+}

+ 36 - 0
saas-account/src/main/java/com/persagy/account/pojo/vo/product/SaasProjectProductQueryVO.java

@@ -0,0 +1,36 @@
+package com.persagy.account.pojo.vo.product;
+
+import com.persagy.account.pojo.vo.BaseGroupVO;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021年5月7日 下午2:07:15
+ */
+@Getter
+@Setter
+@ApiModel
+@ToString
+public class SaasProjectProductQueryVO extends BaseGroupVO {
+
+	@ApiModelProperty(value = "集团签约ID", example = "GP1001")
+	private String contractId; //集团签约ID
+	
+	@ApiModelProperty(value = "项目ID", example = "PD1001")
+	private String projectId; // 项目ID
+
+	@ApiModelProperty(value = "产品ID", example = "PD1001")
+	private String productId; // 产品ID
+	
+	@ApiModelProperty(value = "是否展示项目名称,true-展示,默认false", example = "false")
+	private boolean showProjectName = false; // 是否展示项目名称,true-展示,默认false
+
+}

+ 9 - 0
saas-account/src/main/java/com/persagy/account/service/ISaasAccountService.java

@@ -1,6 +1,7 @@
 package com.persagy.account.service;
 package com.persagy.account.service;
 
 
 import java.util.List;
 import java.util.List;
+import java.util.Set;
 
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -36,6 +37,14 @@ public interface ISaasAccountService extends ISuperService<SaasAccount> {
 	List<SaasAccount> queryRoleAccountList(SaasAccount saasAccount, String roleId);
 	List<SaasAccount> queryRoleAccountList(SaasAccount saasAccount, String roleId);
 	
 	
 	/**
 	/**
+     * 根据角色ID获取,账号信息
+     * @param saasAccount
+     * @param roleId
+     * @return
+     */
+	Page<SaasAccount> queryRoleAccountPage(SaasAccount saasAccount, Set<String> roleIds, Integer page, Integer size);
+	
+	/**
 	 * 获取账号列表,此方法会关联出集团名称信息
 	 * 获取账号列表,此方法会关联出集团名称信息
 	 * 
 	 * 
 	 * @param saasAccount
 	 * @param saasAccount

+ 22 - 1
saas-account/src/main/java/com/persagy/account/service/ISaasFunctionService.java

@@ -29,9 +29,30 @@ public interface ISaasFunctionService extends ISuperService<SaasFunction> {
 	 * @param groupCode
 	 * @param groupCode
 	 * @param roleId
 	 * @param roleId
 	 * @param menuId
 	 * @param menuId
+	 * @param pageId
 	 * @return
 	 * @return
 	 */
 	 */
-	List<SaasFunction> queryRoleFunctionList(String groupCode, String roleId, String menuId);
+	List<SaasFunction> queryRoleFunctionList(String groupCode, String roleId, String menuId, String pageId);
+
+	/**
+	 * 验证功能编码的唯一性
+	 * 
+	 * @param menuId
+	 * @param pageId
+	 * @param funCode
+	 * @param funId
+	 * @return
+	 */
+	boolean validFunCode(String menuId, String pageId, String funCode, String funId);
+
+	/**
+	 * 获取此产品页面再此账号下所拥有的的页面权限
+	 * @param accountId
+	 * @param menuId
+	 * @param pageId
+	 * @return
+	 */
+	List<SaasFunction> queryProductPageFunctions(String accountId, String menuId, String pageId);
 
 
 }
 }
 
 

+ 17 - 0
saas-account/src/main/java/com/persagy/account/service/ISaasGroupProductDetailService.java

@@ -0,0 +1,17 @@
+package com.persagy.account.service;
+
+import com.persagy.common.service.ISuperService;
+import com.persagy.account.pojo.dto.SaasGroupProductDetail;
+
+/**
+ * 集团签约产品详情
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021-05-07 12:02:40
+ */
+public interface ISaasGroupProductDetailService extends ISuperService<SaasGroupProductDetail> {
+
+}
+

+ 46 - 0
saas-account/src/main/java/com/persagy/account/service/ISaasGroupProductService.java

@@ -0,0 +1,46 @@
+package com.persagy.account.service;
+
+import com.persagy.common.service.ISuperService;
+
+import java.util.Set;
+
+import com.persagy.account.pojo.dto.SaasGroupProduct;
+
+/**
+ * 集团签约产品
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021-05-07 12:02:40
+ */
+public interface ISaasGroupProductService extends ISuperService<SaasGroupProduct> {
+
+	/**
+	 * 创建产品集团签约信息
+	 * 
+	 * @param saasGroupProduct
+	 * @param pageIds
+	 * @return
+	 */
+	boolean createSaasGroupProduct(SaasGroupProduct saasGroupProduct, Set<String> pageIds);
+
+	/**
+	 * 更新产品集团签约信息,页面信息会先删除在新增
+	 * 
+	 * @param saasGroupProduct
+	 * @param pageIds
+	 * @return
+	 */
+	boolean updateSaasGroupProduct(SaasGroupProduct saasGroupProduct, Set<String> pageIds);
+
+	/**
+	 * 删除产品集团签约信息
+	 * 
+	 * @param contractId 签约ID
+	 * @return
+	 */
+	boolean deleteSaasGroupProduct(String contractId);
+
+}
+

+ 26 - 0
saas-account/src/main/java/com/persagy/account/service/ISaasMenuService.java

@@ -4,6 +4,7 @@ import java.util.List;
 
 
 import com.persagy.common.service.ISuperService;
 import com.persagy.common.service.ISuperService;
 import com.persagy.account.pojo.dto.SaasMenu;
 import com.persagy.account.pojo.dto.SaasMenu;
+import com.persagy.account.pojo.vo.menu.SaasMenuSort;
 
 
 /**
 /**
  * 菜单信息
  * 菜单信息
@@ -33,5 +34,30 @@ public interface ISaasMenuService extends ISuperService<SaasMenu> {
 	 */
 	 */
 	List<SaasMenu> queryMenuAndEmptyFunction(String groupCode, String roleId);
 	List<SaasMenu> queryMenuAndEmptyFunction(String groupCode, String roleId);
 
 
+	/**
+	 * 批量更新菜单的顺序
+	 * 
+	 * @param menus
+	 * @return
+	 */
+	boolean sortSaasMenu(List<SaasMenuSort> menus);
+
+	/**
+	 * 验证菜单编码在应用下的唯一性
+	 * 
+	 * @param appId
+	 * @param menuCode
+	 * @param menuId
+	 * @return
+	 */
+	boolean validMenuCode(String appId, String menuCode, String menuId);
+	
+	/**
+	 * 创建菜单,同时设置产品是否已关联菜单
+	 * @param saasMenu
+	 * @return
+	 */
+	boolean createSaasMenu(SaasMenu saasMenu);
+	
 }
 }
 
 

+ 51 - 0
saas-account/src/main/java/com/persagy/account/service/ISaasProductPageService.java

@@ -0,0 +1,51 @@
+package com.persagy.account.service;
+
+import java.util.List;
+
+import com.persagy.account.pojo.dto.SaasProductPage;
+import com.persagy.common.service.ISuperService;
+
+/**
+ * 产品页面配置
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021-05-06 10:12:47
+ */
+public interface ISaasProductPageService extends ISuperService<SaasProductPage> {
+
+	/**
+	 * 获取集团所签约的页面集合
+	 * 
+	 * @param groupCode
+	 * @param productId
+	 * @param angle
+	 * @return
+	 */
+	List<SaasProductPage> queryGroupProductPages(String groupCode, String productId, String angle);
+	
+	/**
+	 * 获取项目所签约的此产品的页面集合
+	 * 
+	 * @param groupCode
+	 * @param projectId
+	 * @param productId
+	 * @param angle
+	 * @return
+	 */
+	List<SaasProductPage> queryProjectProductPages(String groupCode, String projectId, String productId, String angle);
+
+	/**
+	 * 获取此账号所拥有的页面权限
+	 * @param accountId
+	 * @param productId
+	 * @param menuId
+	 * @param parentId
+	 * @param angle
+	 * @return
+	 */
+	List<SaasProductPage> queryAccountProductPages(String accountId, String productId, String menuId, String parentId, String angle);
+
+}
+

+ 42 - 0
saas-account/src/main/java/com/persagy/account/service/ISaasProjectProductService.java

@@ -0,0 +1,42 @@
+package com.persagy.account.service;
+
+import com.persagy.common.service.ISuperService;
+
+import java.util.List;
+import java.util.Set;
+
+import com.persagy.account.pojo.dto.SaasProjectProduct;
+
+/**
+ * 项目签约产品
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021-05-07 12:02:40
+ */
+public interface ISaasProjectProductService extends ISuperService<SaasProjectProduct> {
+
+	/**
+	 * 查询产品项目签约信息,携带出项目名称
+	 * @param contractId
+	 * @param groupCode
+	 * @param projectId
+	 * @param productId
+	 * @return
+	 */
+	List<SaasProjectProduct> querySaasProjectProductList(String contractId, String groupCode, String projectId, String productId);
+
+	/**
+	 * 替换该产品集团下的签约项目,删除新增
+	 * 
+	 * @param contractId
+	 * @param groupCode
+	 * @param productId
+	 * @param projectIds
+	 * @return
+	 */
+	boolean replaceSaasProjectProduct(String contractId, String groupCode, String productId, Set<String> projectIds);
+
+}
+

+ 22 - 1
saas-account/src/main/java/com/persagy/account/service/impl/SaasAccountServiceImpl.java

@@ -1,12 +1,14 @@
 package com.persagy.account.service.impl;
 package com.persagy.account.service.impl;
 
 
 import java.util.List;
 import java.util.List;
+import java.util.Set;
 
 
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 import com.persagy.account.mapper.SaasAccountMapper;
 import com.persagy.account.mapper.SaasAccountMapper;
 import com.persagy.account.pojo.dto.SaasAccount;
 import com.persagy.account.pojo.dto.SaasAccount;
 import com.persagy.account.service.ISaasAccountService;
 import com.persagy.account.service.ISaasAccountService;
@@ -42,7 +44,26 @@ public class SaasAccountServiceImpl extends SuperServiceImpl<SaasAccountMapper,
 	
 	
 	@Override
 	@Override
 	public List<SaasAccount> queryRoleAccountList(SaasAccount saasAccount, String roleId) {
 	public List<SaasAccount> queryRoleAccountList(SaasAccount saasAccount, String roleId) {
-		return this.baseMapper.queryRoleAccountList(saasAccount, roleId);
+		return this.baseMapper.queryRoleAccountList(saasAccount, Sets.newHashSet(roleId), null, null);
+	}
+	
+	@Override
+	public Page<SaasAccount> queryRoleAccountPage(SaasAccount saasAccount, Set<String> roleIds, Integer page, Integer size) {
+		Page<SaasAccount> pageRecord = new Page<SaasAccount>();
+		int startRow = (page - 1) * size;
+    	pageRecord.setCurrent(page);
+		pageRecord.setSize(size);
+		List<SaasAccount> list = this.baseMapper.queryRoleAccountList(saasAccount, roleIds, startRow, size);
+		if (CollectionUtil.isEmpty(list)) {
+			pageRecord.setRecords(Lists.newArrayList());
+			pageRecord.setTotal(0);
+			return pageRecord;
+		}
+		// 查询出总记录数大小
+		int count = this.baseMapper.countRoleAccountList(saasAccount, roleIds);
+		pageRecord.setRecords(list);
+		pageRecord.setTotal(count);
+		return pageRecord;
 	}
 	}
 	
 	
 	@Override
 	@Override

+ 28 - 2
saas-account/src/main/java/com/persagy/account/service/impl/SaasFunctionServiceImpl.java

@@ -5,6 +5,8 @@ import java.util.List;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 
 
 import com.persagy.common.service.impl.SuperServiceImpl;
 import com.persagy.common.service.impl.SuperServiceImpl;
+import com.persagy.common.utils.StringUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.persagy.account.mapper.SaasFunctionMapper;
 import com.persagy.account.mapper.SaasFunctionMapper;
 import com.persagy.account.pojo.dto.SaasFunction;
 import com.persagy.account.pojo.dto.SaasFunction;
 import com.persagy.account.service.ISaasFunctionService;
 import com.persagy.account.service.ISaasFunctionService;
@@ -26,8 +28,32 @@ public class SaasFunctionServiceImpl extends SuperServiceImpl<SaasFunctionMapper
 	}
 	}
 
 
 	@Override
 	@Override
-	public List<SaasFunction> queryRoleFunctionList(String groupCode, String roleId, String menuId) {
-		return this.baseMapper.queryRoleFunctionList(groupCode, roleId, menuId);
+	public List<SaasFunction> queryRoleFunctionList(String groupCode, String roleId, String menuId, String pageId) {
+		return this.baseMapper.queryRoleFunctionList(groupCode, roleId, menuId, pageId);
+	}
+
+	@Override
+	public boolean validFunCode(String menuId, String pageId, String funCode, String funId) {
+		if (StringUtil.isNotBlank(menuId)) {
+			LambdaQueryWrapper<SaasFunction> queryWrapper = new SaasFunction.Builder().createQueryWrapper().menuIdEq(menuId).funCodeEq(funCode).builderQueryWrapper();
+			SaasFunction selectOne = this.baseMapper.selectOne(queryWrapper);
+			if (selectOne != null && (StringUtil.isBlank(funId) || !funId.equals(selectOne.getId()))) {
+				return false;
+			}
+		}
+		if (StringUtil.isNotBlank(pageId)) {
+			LambdaQueryWrapper<SaasFunction> queryWrapper = new SaasFunction.Builder().createQueryWrapper().pageIdEq(pageId).funCodeEq(funCode).builderQueryWrapper();
+			SaasFunction selectOne = this.baseMapper.selectOne(queryWrapper);
+			if (selectOne != null && (StringUtil.isBlank(funId) || !funId.equals(selectOne.getId()))) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	@Override
+	public List<SaasFunction> queryProductPageFunctions(String accountId, String menuId, String pageId) {
+		return this.baseMapper.queryProductPageFunctions(accountId, menuId, pageId);
 	}
 	}
 
 
 }
 }

+ 21 - 0
saas-account/src/main/java/com/persagy/account/service/impl/SaasGroupProductDetailServiceImpl.java

@@ -0,0 +1,21 @@
+package com.persagy.account.service.impl;
+
+import org.springframework.stereotype.Service;
+
+import com.persagy.common.service.impl.SuperServiceImpl;
+import com.persagy.account.mapper.SaasGroupProductDetailMapper;
+import com.persagy.account.pojo.dto.SaasGroupProductDetail;
+import com.persagy.account.service.ISaasGroupProductDetailService;
+
+/**
+ * 集团签约产品详情
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021-05-07 12:02:40
+ */
+@Service
+public class SaasGroupProductDetailServiceImpl extends SuperServiceImpl<SaasGroupProductDetailMapper, SaasGroupProductDetail> implements ISaasGroupProductDetailService {
+
+}

+ 71 - 0
saas-account/src/main/java/com/persagy/account/service/impl/SaasGroupProductServiceImpl.java

@@ -0,0 +1,71 @@
+package com.persagy.account.service.impl;
+
+import java.util.Set;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
+import com.persagy.account.mapper.SaasGroupProductDetailMapper;
+import com.persagy.account.mapper.SaasGroupProductMapper;
+import com.persagy.account.pojo.dto.SaasGroupProduct;
+import com.persagy.account.pojo.dto.SaasGroupProductDetail;
+import com.persagy.account.service.ISaasGroupProductService;
+import com.persagy.common.service.impl.SuperServiceImpl;
+
+/**
+ * 集团签约产品
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021-05-07 12:02:40
+ */
+@Service
+public class SaasGroupProductServiceImpl extends SuperServiceImpl<SaasGroupProductMapper, SaasGroupProduct> implements ISaasGroupProductService {
+
+	@Autowired
+	private SaasGroupProductDetailMapper saasGroupProductDetailMapper;
+	
+	@Override
+	@Transactional
+	public boolean createSaasGroupProduct(SaasGroupProduct saasGroupProduct, Set<String> pageIds) {
+		// 优先创建产品集团签约信息
+		Integer result = this.baseMapper.insert(saasGroupProduct);
+		if (SqlHelper.retBool(result)) {
+			// 创建签约信息与产品页面的关联关系
+			result = this.saasGroupProductDetailMapper.insertSaasGroupProductDetail(saasGroupProduct.getId(), pageIds);
+		}
+		return SqlHelper.retBool(result);
+	}
+
+	@Override
+	@Transactional
+	public boolean updateSaasGroupProduct(SaasGroupProduct saasGroupProduct, Set<String> pageIds) {
+		// 优先更新产品集团签约信息
+		Integer result = this.baseMapper.updateById(saasGroupProduct);
+		if (SqlHelper.retBool(result)) {
+			// 删除签约信息与产品页面的关联关系
+			LambdaQueryWrapper<SaasGroupProductDetail> queryWrapper = new SaasGroupProductDetail.BuilderQueryWrapper().contractIdEq(saasGroupProduct.getId()).builder();
+			this.saasGroupProductDetailMapper.delete(queryWrapper);
+			// 创建签约信息与产品页面的关联关系
+			result = this.saasGroupProductDetailMapper.insertSaasGroupProductDetail(saasGroupProduct.getId(), pageIds);
+		}
+		return SqlHelper.retBool(result);
+	}
+
+	@Override
+	@Transactional
+	public boolean deleteSaasGroupProduct(String contractId) {
+		Integer result = this.baseMapper.deleteById(contractId);
+		if (SqlHelper.retBool(result)) {
+			// 删除签约信息与产品页面的关联关系
+			LambdaQueryWrapper<SaasGroupProductDetail> pageWrapper = new SaasGroupProductDetail.BuilderQueryWrapper().contractIdEq(contractId).builder();
+			result = this.saasGroupProductDetailMapper.delete(pageWrapper);
+		}
+		return SqlHelper.retBool(result);
+	}
+
+}

+ 44 - 1
saas-account/src/main/java/com/persagy/account/service/impl/SaasMenuServiceImpl.java

@@ -2,12 +2,21 @@ package com.persagy.account.service.impl;
 
 
 import java.util.List;
 import java.util.List;
 
 
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 
-import com.persagy.common.service.impl.SuperServiceImpl;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
 import com.persagy.account.mapper.SaasMenuMapper;
 import com.persagy.account.mapper.SaasMenuMapper;
+import com.persagy.account.mapper.SaasProductMapper;
 import com.persagy.account.pojo.dto.SaasMenu;
 import com.persagy.account.pojo.dto.SaasMenu;
+import com.persagy.account.pojo.dto.SaasProduct;
+import com.persagy.account.pojo.vo.menu.SaasMenuSort;
 import com.persagy.account.service.ISaasMenuService;
 import com.persagy.account.service.ISaasMenuService;
+import com.persagy.common.constant.SaasCommonConstant;
+import com.persagy.common.service.impl.SuperServiceImpl;
+import com.persagy.common.utils.StringUtil;
 
 
 /**
 /**
  * 菜单信息
  * 菜单信息
@@ -20,6 +29,9 @@ import com.persagy.account.service.ISaasMenuService;
 @Service
 @Service
 public class SaasMenuServiceImpl extends SuperServiceImpl<SaasMenuMapper, SaasMenu> implements ISaasMenuService {
 public class SaasMenuServiceImpl extends SuperServiceImpl<SaasMenuMapper, SaasMenu> implements ISaasMenuService {
 	
 	
+	@Autowired
+	private SaasProductMapper saasProductMapper;
+	
 	@Override
 	@Override
 	public List<SaasMenu> querySaasMenuTree(String accountId, String groupCode, String appId, Integer valid) {
 	public List<SaasMenu> querySaasMenuTree(String accountId, String groupCode, String appId, Integer valid) {
 		return this.baseMapper.querySaasMenuTree(accountId, groupCode, appId, valid);
 		return this.baseMapper.querySaasMenuTree(accountId, groupCode, appId, valid);
@@ -30,4 +42,35 @@ public class SaasMenuServiceImpl extends SuperServiceImpl<SaasMenuMapper, SaasMe
 		return this.baseMapper.queryMenuAndEmptyFunction(groupCode, roleId);
 		return this.baseMapper.queryMenuAndEmptyFunction(groupCode, roleId);
 	}
 	}
 
 
+	@Override
+	@Transactional
+	public boolean sortSaasMenu(List<SaasMenuSort> menus) {
+		Integer result = this.baseMapper.sortSaasMenu(menus);
+		return SqlHelper.retBool(result);
+	}
+
+	@Override
+	public boolean validMenuCode(String appId, String menuCode, String menuId) {
+		LambdaQueryWrapper<SaasMenu> queryWrapper = new SaasMenu.Builder().createQueryWrapper().appIdEq(appId).menuCodeEq(menuCode).builderQueryWrapper();
+		SaasMenu selectOne = this.baseMapper.selectOne(queryWrapper);
+		if (selectOne != null && (StringUtil.isBlank(menuId) || !menuId.equals(selectOne.getId()))) {
+			return false;
+		}
+		return true;
+	}
+
+	@Override
+	@Transactional
+	public boolean createSaasMenu(SaasMenu saasMenu) {
+		Integer result = this.baseMapper.insert(saasMenu);
+		if (SqlHelper.retBool(result) && StringUtil.isNotBlank(saasMenu.getProductId())) {
+			// 更新产品是否已关联菜单字段
+			SaasProduct saasProduct = new SaasProduct();
+			saasProduct.setId(saasMenu.getProductId());
+			saasProduct.setProductMenu(SaasCommonConstant.STR_STATUS_1);
+			result = this.saasProductMapper.updateById(saasProduct);
+		}
+		return SqlHelper.retBool(result);
+	}
+
 }
 }

+ 38 - 0
saas-account/src/main/java/com/persagy/account/service/impl/SaasProductPageServiceImpl.java

@@ -0,0 +1,38 @@
+package com.persagy.account.service.impl;
+
+import java.util.List;
+
+import org.springframework.stereotype.Service;
+
+import com.persagy.common.service.impl.SuperServiceImpl;
+import com.persagy.account.mapper.SaasProductPageMapper;
+import com.persagy.account.pojo.dto.SaasProductPage;
+import com.persagy.account.service.ISaasProductPageService;
+
+/**
+ * 产品页面配置
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021-05-06 10:12:47
+ */
+@Service
+public class SaasProductPageServiceImpl extends SuperServiceImpl<SaasProductPageMapper, SaasProductPage> implements ISaasProductPageService {
+
+	@Override
+	public List<SaasProductPage> queryGroupProductPages(String groupCode, String productId, String angle) {
+		return this.baseMapper.queryGroupProductPages(groupCode, productId, angle);
+	}
+
+	@Override
+	public List<SaasProductPage> queryProjectProductPages(String groupCode, String projectId, String productId, String angle) {
+		return this.baseMapper.queryProjectProductPages(groupCode, projectId, productId, angle);
+	}
+
+	@Override
+	public List<SaasProductPage> queryAccountProductPages(String accountId, String productId, String menuId, String parentId, String angle) {
+		return this.baseMapper.queryAccountProductPages(accountId, productId, menuId, parentId, angle);
+	}
+
+}

+ 43 - 0
saas-account/src/main/java/com/persagy/account/service/impl/SaasProjectProductServiceImpl.java

@@ -0,0 +1,43 @@
+package com.persagy.account.service.impl;
+
+import java.util.List;
+import java.util.Set;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.persagy.common.service.impl.SuperServiceImpl;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
+import com.persagy.account.mapper.SaasProjectProductMapper;
+import com.persagy.account.pojo.dto.SaasProjectProduct;
+import com.persagy.account.service.ISaasProjectProductService;
+
+/**
+ * 项目签约产品
+ * 
+ * @version 1.0.0
+ * @company persagy 
+ * @author zhangqiankun
+ * @date 2021-05-07 12:02:40
+ */
+@Service
+public class SaasProjectProductServiceImpl extends SuperServiceImpl<SaasProjectProductMapper, SaasProjectProduct> implements ISaasProjectProductService {
+
+	@Override
+	public List<SaasProjectProduct> querySaasProjectProductList(String contractId, String groupCode, String projectId, String productId) {
+		return this.baseMapper.querySaasProjectProductList(contractId, groupCode, projectId, productId);
+	}
+
+	@Override
+	@Transactional
+	public boolean replaceSaasProjectProduct(String contractId, String groupCode, String productId, Set<String> projectIds) {
+		// 删除
+		LambdaQueryWrapper<SaasProjectProduct> queryWrapper = new SaasProjectProduct.BuilderQueryWrapper().contractIdEq(contractId).groupCodeEq(groupCode).productIdEq(productId).builder();
+		this.baseMapper.delete(queryWrapper);
+		// 新增
+		Integer result = this.baseMapper.batchInsertSaasProjectProduct(contractId, groupCode, productId, projectIds);
+		return SqlHelper.retBool(result);
+	}
+
+}

+ 29 - 2
saas-account/src/main/resources/mapper/SaasAccountMapper.xml

@@ -64,7 +64,9 @@
 		SELECT N.* FROM (SELECT DISTINCT ACCOUNT_ID FROM saas_account_role 
 		SELECT N.* FROM (SELECT DISTINCT ACCOUNT_ID FROM saas_account_role 
 		<where>
 		<where>
 			<if test="account.groupCode != null and account.groupCode.trim().length() != 0">AND GROUP_CODE = #{account.groupCode}</if>
 			<if test="account.groupCode != null and account.groupCode.trim().length() != 0">AND GROUP_CODE = #{account.groupCode}</if>
-			<if test="roleId != null and roleId.trim().length() != 0">AND ROLE_ID = #{roleId}</if>
+			<if test="roleIds != null">AND ROLE_ID IN
+				<foreach collection="roleIds" index="index" item="roleId" open="(" close=")" separator="," >#{roleId}</foreach>
+			</if>
 		</where>) M JOIN (SELECT <include refid="Base_Sql_Columns"></include> FROM saas_account
 		</where>) M JOIN (SELECT <include refid="Base_Sql_Columns"></include> FROM saas_account
 		<where>
 		<where>
 			<if test="account.id != null and account.id.trim().length() != 0">AND ID = #{account.id}</if>
 			<if test="account.id != null and account.id.trim().length() != 0">AND ID = #{account.id}</if>
@@ -79,6 +81,31 @@
 			<if test="account.updateUser != null and account.updateUser.trim().length() != 0">AND UPDATE_USER = #{account.updateUser}</if>
 			<if test="account.updateUser != null and account.updateUser.trim().length() != 0">AND UPDATE_USER = #{account.updateUser}</if>
 			<if test="account.valid != null">AND VALID = #{account.valid}</if>
 			<if test="account.valid != null">AND VALID = #{account.valid}</if>
 		</where>) N ON M.ACCOUNT_ID = N.ID ORDER BY N.UPDATE_TIME DESC, N.ID DESC 
 		</where>) N ON M.ACCOUNT_ID = N.ID ORDER BY N.UPDATE_TIME DESC, N.ID DESC 
+		<if test="startRow != null and size != null">LIMIT #{startRow}, #{size} </if>
 	</select>
 	</select>
-	
+
+	<!-- 和 queryRoleAccountList 方法必须保持同步 -->
+	<select id="countRoleAccountList" resultType="java.lang.Integer">
+		SELECT COUNT(*) FROM (SELECT DISTINCT ACCOUNT_ID FROM saas_account_role 
+		<where>
+			<if test="account.groupCode != null and account.groupCode.trim().length() != 0">AND GROUP_CODE = #{account.groupCode}</if>
+			<if test="roleIds != null">AND ROLE_ID IN
+				<foreach collection="roleIds" index="index" item="roleId" open="(" close=")" separator="," >#{roleId}</foreach>
+			</if>
+		</where>) M JOIN (SELECT ID FROM saas_account
+		<where>
+			<if test="account.id != null and account.id.trim().length() != 0">AND ID = #{account.id}</if>
+			<if test="account.groupCode != null and account.groupCode.trim().length() != 0">AND GROUP_CODE = #{account.groupCode}</if>
+			<if test="account.appId != null and account.appId.trim().length() != 0">AND TERMINAL LIKE CONCAT('%', #{account.appId}, '%')</if>
+			<if test="account.phoneNum != null and account.phoneNum.trim().length() != 0">AND PHONE_NUM = #{account.phoneNum}</if>
+			<if test="account.mail != null and account.mail.trim().length() != 0">AND MAIL = #{account.mail}</if>
+			<if test="account.username != null and account.username.trim().length() != 0">AND USERNAME LIKE CONCAT('%', #{account.username}, '%')</if>
+			<if test="account.accountType != null and account.accountType.trim().length() != 0">AND ACCOUNT_TYPE = #{account.accountType}</if>
+			<if test="account.accountBelong != null and account.accountBelong.trim().length() != 0">AND ACCOUNT_BELONG = #{account.accountBelong}</if>
+			<if test="account.accountSource != null and account.accountSource.trim().length() != 0">AND ACCOUNT_SOURCE = #{account.accountSource}</if>
+			<if test="account.updateUser != null and account.updateUser.trim().length() != 0">AND UPDATE_USER = #{account.updateUser}</if>
+			<if test="account.valid != null">AND VALID = #{account.valid}</if>
+		</where>) N ON M.ACCOUNT_ID = N.ID
+	</select>
+		
 </mapper>
 </mapper>

+ 21 - 1
saas-account/src/main/resources/mapper/SaasFunctionMapper.xml

@@ -6,6 +6,7 @@
 	<resultMap id="BaseResultMap" type="com.persagy.account.pojo.dto.SaasFunction">
 	<resultMap id="BaseResultMap" type="com.persagy.account.pojo.dto.SaasFunction">
 		<result column="ID" property="id" jdbcType="VARCHAR" />
 		<result column="ID" property="id" jdbcType="VARCHAR" />
 		<result column="MENU_ID" property="menuId" jdbcType="VARCHAR" />
 		<result column="MENU_ID" property="menuId" jdbcType="VARCHAR" />
+		<result column="PAGE_ID" property="pageId" jdbcType="VARCHAR" />
 		<result column="FUN_CODE" property="funCode" jdbcType="VARCHAR" />
 		<result column="FUN_CODE" property="funCode" jdbcType="VARCHAR" />
 		<result column="FUN_NAME" property="funName" jdbcType="VARCHAR" />
 		<result column="FUN_NAME" property="funName" jdbcType="VARCHAR" />
 		<result column="FUN_URL" property="funUrl" jdbcType="VARCHAR" />
 		<result column="FUN_URL" property="funUrl" jdbcType="VARCHAR" />
@@ -19,7 +20,7 @@
 	</resultMap>
 	</resultMap>
 	
 	
 	<sql id="Base_Sql_Column">
 	<sql id="Base_Sql_Column">
-		ID, MENU_ID, FUN_CODE, FUN_NAME, FUN_URL, FUN_SORT, REMARK
+		ID, MENU_ID, PAGE_ID, FUN_CODE, FUN_NAME, FUN_URL, FUN_SORT, REMARK
 	</sql>
 	</sql>
 	
 	
 	<select id="queryAccountFunctionList" resultMap="BaseResultMap">
 	<select id="queryAccountFunctionList" resultMap="BaseResultMap">
@@ -44,11 +45,30 @@
 			<if test="groupCode != null and groupCode.trim().length() != 0">AND GROUP_CODE = #{groupCode}</if>
 			<if test="groupCode != null and groupCode.trim().length() != 0">AND GROUP_CODE = #{groupCode}</if>
 			<if test="roleId != null and roleId.trim().length() != 0">AND ROLE_ID = #{roleId}</if>
 			<if test="roleId != null and roleId.trim().length() != 0">AND ROLE_ID = #{roleId}</if>
 			<if test="menuId != null and menuId.trim().length() != 0">AND MENU_ID = #{menuId}</if>
 			<if test="menuId != null and menuId.trim().length() != 0">AND MENU_ID = #{menuId}</if>
+			<if test="pageId != null and pageId.trim().length() != 0">AND PAGE_ID = #{pageId}</if>
 		</where>) M
 		</where>) M
 		JOIN (SELECT <include refid="Base_Sql_Column"></include> FROM saas_function 
 		JOIN (SELECT <include refid="Base_Sql_Column"></include> FROM saas_function 
 		<where>
 		<where>
 			<if test="menuId != null and menuId.trim().length() != 0">AND MENU_ID = #{menuId}</if>
 			<if test="menuId != null and menuId.trim().length() != 0">AND MENU_ID = #{menuId}</if>
+			<if test="pageId != null and pageId.trim().length() != 0">AND PAGE_ID = #{pageId}</if>
 		</where>) N ON N.ID = M.FUNCTION_ID ORDER BY N.FUN_SORT
 		</where>) N ON N.ID = M.FUNCTION_ID ORDER BY N.FUN_SORT
 	</select>
 	</select>
 	
 	
+	<select id="queryProductPageFunctions" resultMap="BaseResultMap">
+		SELECT DISTINCT P.* FROM (SELECT ROLE_ID FROM saas_account_role 
+		<where>
+			<if test="accountId != null and accountId.trim().length() != 0">AND ACCOUNT_ID = #{accountId}</if>
+		</where>) M 
+		JOIN (SELECT ROLE_ID, FUNCTION_ID FROM saas_role_menu 
+		<where>
+			<if test="menuId != null and menuId.trim().length() != 0">AND MENU_ID = #{menuId}</if>
+			<if test="pageId != null and pageId.trim().length() != 0">AND PAGE_ID = #{pageId}</if>
+		</where>) N ON M.ROLE_ID = N.ROLE_ID
+		JOIN (SELECT <include refid="Base_Sql_Column"></include> FROM saas_function 
+		<where>
+			<!-- 此处不能添加菜单ID作为查询条件 -->
+			<if test="pageId != null and pageId.trim().length() != 0">AND PAGE_ID = #{pageId}</if>
+		</where>) P ON P.ID = N.FUNCTION_ID ORDER BY P.FUN_SORT
+	</select>
+	
 </mapper>
 </mapper>

+ 18 - 0
saas-account/src/main/resources/mapper/SaasGroupProductDetailMapper.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="com.persagy.account.mapper.SaasGroupProductDetailMapper">
+    
+	<resultMap id="BaseResultMap" type="com.persagy.account.pojo.dto.SaasGroupProductDetail">
+		<result column="CONTRACT_ID" property="contractId" jdbcType="VARCHAR" />
+		<result column="PAGE_ID" property="pageId" jdbcType="VARCHAR" />
+	</resultMap>
+	
+	<insert id="batchInsertSaasProjectProduct">
+		INSERT INTO saas_group_product_detail (CONTRACT_ID, PAGE_ID) VALUES
+		<foreach collection="pageIds" index="index" item="pageId" separator="," >
+			(#{contractId}, #{pageId})
+		</foreach>
+	</insert>
+	
+</mapper>

+ 20 - 0
saas-account/src/main/resources/mapper/SaasGroupProductMapper.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="com.persagy.account.mapper.SaasGroupProductMapper">
+    
+	<resultMap id="BaseResultMap" type="com.persagy.account.pojo.dto.SaasGroupProduct">
+		<result column="ID" property="id" jdbcType="VARCHAR" />
+		<result column="GROUP_CODE" property="groupCode" jdbcType="VARCHAR" />
+		<result column="PRODUCT_ID" property="productId" jdbcType="VARCHAR" />
+		<result column="PRODUCT_CODE" property="productCode" jdbcType="VARCHAR" />
+		<result column="PRODUCT_RANGE" property="productRange" jdbcType="VARCHAR" />
+		<result column="PRODUCT_CONTRACT" property="productContract" jdbcType="DATE" />
+		<result column="VALID_START_TIME" property="validStartTime" jdbcType="DATE" />
+		<result column="VALID_END_TIME" property="validEndTime" jdbcType="DATE" />
+		<result column="VALID_LAST" property="validLast" jdbcType="VARCHAR" />
+		<result column="UPDATE_TIME" property="updateTime" jdbcType="DATE" />
+		<result column="UPDATE_USER" property="updateUser" jdbcType="VARCHAR" />
+		<result column="REMARK" property="remark" jdbcType="VARCHAR" />
+	</resultMap>
+</mapper>

+ 13 - 4
saas-account/src/main/resources/mapper/SaasMenuMapper.xml

@@ -7,6 +7,7 @@
 		<result column="ID" property="id" jdbcType="VARCHAR" />
 		<result column="ID" property="id" jdbcType="VARCHAR" />
 		<result column="PARENT_ID" property="parentId" jdbcType="VARCHAR" />
 		<result column="PARENT_ID" property="parentId" jdbcType="VARCHAR" />
 		<result column="APP_ID" property="appId" jdbcType="VARCHAR" />
 		<result column="APP_ID" property="appId" jdbcType="VARCHAR" />
+		<result column="PRODUCT_ID" property="productId" jdbcType="VARCHAR" />
 		<result column="MENU_CODE" property="menuCode" jdbcType="VARCHAR" />
 		<result column="MENU_CODE" property="menuCode" jdbcType="VARCHAR" />
 		<result column="MENU_NAME" property="menuName" jdbcType="VARCHAR" />
 		<result column="MENU_NAME" property="menuName" jdbcType="VARCHAR" />
 		<result column="MENU_URL" property="menuUrl" jdbcType="VARCHAR" />
 		<result column="MENU_URL" property="menuUrl" jdbcType="VARCHAR" />
@@ -18,7 +19,6 @@
 		<result column="UPDATE_TIME" property="updateTime" jdbcType="DATE" />
 		<result column="UPDATE_TIME" property="updateTime" jdbcType="DATE" />
 		<result column="UPDATE_USER" property="updateUser" jdbcType="VARCHAR" />
 		<result column="UPDATE_USER" property="updateUser" jdbcType="VARCHAR" />
 		<result column="REMARK" property="remark" jdbcType="VARCHAR" />
 		<result column="REMARK" property="remark" jdbcType="VARCHAR" />
-		<result column="MENU_SORT" property="menuSort" jdbcType="INTEGER" />
 		<!-- 额外信息 -->
 		<!-- 额外信息 -->
 		<result column="GROUP_CODE" property="groupCode" jdbcType="VARCHAR" />
 		<result column="GROUP_CODE" property="groupCode" jdbcType="VARCHAR" />
 		<result column="ACCOUNT_ID" property="accountId" jdbcType="VARCHAR" />
 		<result column="ACCOUNT_ID" property="accountId" jdbcType="VARCHAR" />
@@ -26,6 +26,10 @@
 		<result column="MENU_ID" property="menuId" jdbcType="VARCHAR" />
 		<result column="MENU_ID" property="menuId" jdbcType="VARCHAR" />
 	</resultMap>
 	</resultMap>
 
 
+	<sql id="Base_Sql_Column">
+		ID, PARENT_ID, APP_ID, PRODUCT_ID, MENU_CODE, MENU_NAME, MENU_URL, URL_TYPE, MENU_ICON, MENU_SORT, MENU_TYPE, REMARK
+	</sql>
+	
 	<!-- 查询所被允许的账号菜单权限 -->
 	<!-- 查询所被允许的账号菜单权限 -->
 	<select id="querySaasMenuTree" resultMap="BaseResultMap">
 	<select id="querySaasMenuTree" resultMap="BaseResultMap">
 		SELECT P.GROUP_CODE, P.ACCOUNT_ID, Q.* FROM (SELECT DISTINCT M.GROUP_CODE, M.ACCOUNT_ID, S.MENU_ID FROM 
 		SELECT P.GROUP_CODE, P.ACCOUNT_ID, Q.* FROM (SELECT DISTINCT M.GROUP_CODE, M.ACCOUNT_ID, S.MENU_ID FROM 
@@ -43,8 +47,7 @@
 		<where>
 		<where>
 			<if test="groupCode != null and groupCode.trim().length() != 0">AND GROUP_CODE = #{groupCode}</if>
 			<if test="groupCode != null and groupCode.trim().length() != 0">AND GROUP_CODE = #{groupCode}</if>
 		</where>) S ON M.ROLE_ID = S.ROLE_ID) P
 		</where>) S ON M.ROLE_ID = S.ROLE_ID) P
-		JOIN (SELECT ID AS MENU_ID, PARENT_ID, APP_ID, MENU_CODE, MENU_NAME, MENU_URL, URL_TYPE, MENU_ICON, MENU_SORT, MENU_TYPE, REMARK 
-			FROM saas_menu
+		JOIN (SELECT ID AS MENU_ID, <include refid="Base_Sql_Column"></include> FROM saas_menu
 		<where>
 		<where>
 			<if test="appId != null and appId.trim().length() != 0">AND APP_ID = #{appId}</if>
 			<if test="appId != null and appId.trim().length() != 0">AND APP_ID = #{appId}</if>
 		</where>) Q ON P.MENU_ID = Q.MENU_ID ORDER BY Q.MENU_SORT
 		</where>) Q ON P.MENU_ID = Q.MENU_ID ORDER BY Q.MENU_SORT
@@ -52,7 +55,7 @@
 	
 	
 	<!-- 查询出此角色下,所有不存在功能点的菜单集合 -->
 	<!-- 查询出此角色下,所有不存在功能点的菜单集合 -->
 	<select id="queryMenuAndEmptyFunction" resultMap="BaseResultMap">
 	<select id="queryMenuAndEmptyFunction" resultMap="BaseResultMap">
-		SELECT M.MENU_ID, N.ID, N.PARENT_ID, N.APP_ID, N.MENU_CODE, N.MENU_NAME, N.MENU_URL, N.URL_TYPE, N.MENU_ICON, 
+		SELECT M.MENU_ID, N.ID, N.PARENT_ID, N.APP_ID, N.PRODUCT_ID, N.MENU_CODE, N.MENU_NAME, N.MENU_URL, N.URL_TYPE, N.MENU_ICON, 
 			N.MENU_SORT, N.MENU_TYPE, N.REMARK FROM (
 			N.MENU_SORT, N.MENU_TYPE, N.REMARK FROM (
 			SELECT DISTINCT MENU_ID FROM saas_role_menu WHERE (FUNCTION_ID IS NULL OR TRIM(FUNCTION_ID) = '')
 			SELECT DISTINCT MENU_ID FROM saas_role_menu WHERE (FUNCTION_ID IS NULL OR TRIM(FUNCTION_ID) = '')
 			<if test="groupCode != null and groupCode.trim().length() != 0">AND GROUP_CODE = #{groupCode}</if>
 			<if test="groupCode != null and groupCode.trim().length() != 0">AND GROUP_CODE = #{groupCode}</if>
@@ -60,4 +63,10 @@
 		) M LEFT JOIN saas_menu N ON M.MENU_ID = N.ID ORDER BY N.MENU_SORT
 		) M LEFT JOIN saas_menu N ON M.MENU_ID = N.ID ORDER BY N.MENU_SORT
 	</select>
 	</select>
 	
 	
+	<update id="sortSaasMenu">
+		<foreach collection="menus" index="index" item="menu" separator=";" >
+			UPDATE saas_menu SET MENU_SORT = #{menu.menuSort} WHERE ID = #{menu.menuId}
+		</foreach>
+	</update>
+	
 </mapper>
 </mapper>

+ 1 - 0
saas-account/src/main/resources/mapper/SaasProductMapper.xml

@@ -5,6 +5,7 @@
     
     
 	<resultMap id="BaseResultMap" type="com.persagy.account.pojo.dto.SaasProduct">
 	<resultMap id="BaseResultMap" type="com.persagy.account.pojo.dto.SaasProduct">
 		<result column="ID" property="id" jdbcType="VARCHAR" />
 		<result column="ID" property="id" jdbcType="VARCHAR" />
+		<result column="APP_ID" property="appId" jdbcType="VARCHAR" />
 		<result column="PRODUCT_CODE" property="productCode" jdbcType="VARCHAR" />
 		<result column="PRODUCT_CODE" property="productCode" jdbcType="VARCHAR" />
 		<result column="PRODUCT_NAME" property="productName" jdbcType="VARCHAR" />
 		<result column="PRODUCT_NAME" property="productName" jdbcType="VARCHAR" />
 		<result column="PRODUCT_ICON" property="productIcon" jdbcType="VARCHAR" />
 		<result column="PRODUCT_ICON" property="productIcon" jdbcType="VARCHAR" />

+ 80 - 0
saas-account/src/main/resources/mapper/SaasProductPageMapper.xml

@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="com.persagy.account.mapper.SaasProductPageMapper">
+    
+	<resultMap id="BaseResultMap" type="com.persagy.account.pojo.dto.SaasProductPage">
+		<result column="ID" property="id" jdbcType="VARCHAR" />
+		<result column="PRODUCT_ID" property="productId" jdbcType="VARCHAR" />
+		<result column="PARENT_ID" property="parentId" jdbcType="VARCHAR" />
+		<result column="PAGE_NAME" property="pageName" jdbcType="VARCHAR" />
+		<result column="PAGE_ICON" property="pageIcon" jdbcType="VARCHAR" />
+		<result column="PAGE_URL" property="pageUrl" jdbcType="VARCHAR" />
+		<result column="PAGE_ANGLE" property="pageAngle" jdbcType="VARCHAR" />
+		<result column="PAGE_FLOOR" property="pageFloor" jdbcType="VARCHAR" />
+		<result column="PAGE_SORT" property="pageSort" jdbcType="INTEGER" />
+		<result column="CREATE_TIME" property="createTime" jdbcType="DATE" />
+		<result column="UPDATE_TIME" property="updateTime" jdbcType="DATE" />
+		<result column="UPDATE_USER" property="updateUser" jdbcType="VARCHAR" />
+		<result column="REMARK" property="remark" jdbcType="VARCHAR" />
+	</resultMap>
+	
+	<sql id="Base_Sql_Column">
+		ID, PRODUCT_ID, PARENT_ID, PAGE_NAME, PAGE_ICON, PAGE_URL, PAGE_ANGLE, PAGE_FLOOR, PAGE_SORT, CREATE_TIME, UPDATE_TIME, UPDATE_USER, REMARK
+	</sql>
+	
+	<select id="queryGroupProductPages" resultMap="BaseResultMap">
+		SELECT M.* FROM (SELECT ID FROM saas_group_product 
+		<where>
+			<if test="groupCode != null and groupCode.trim().length() != 0">AND GROUP_CODE = #{groupCode}</if>
+			<if test="productId != null and productId.trim().length() != 0">AND PRODUCT_ID = #{productId}</if>
+		</where>) M JOIN saas_group_product_detail N ON M.ID = N.CONTRACT_ID
+		JOIN (SELECT <include refid="Base_Sql_Column"></include> FROM saas_product_page 
+		<where>
+			<if test="productId != null and productId.trim().length() != 0">AND PRODUCT_ID = #{productId}</if>
+			<choose>
+                <when test="angle != null and angle == '1'">AND PAGE_ANGLE IN ('0', '1')</when>
+                <when test="angle != null and angle == '2'">AND PAGE_ANGLE IN ('0', '2')</when>
+            </choose>
+		</where>) P ON P.ID = N.PAGE_ID
+	</select>
+
+	<select id="queryProjectProductPages" resultMap="BaseResultMap">
+		SELECT M.* FROM (SELECT ID FROM saas_project_product 
+		<where>
+			<if test="groupCode != null and groupCode.trim().length() != 0">AND GROUP_CODE = #{groupCode}</if>
+			<if test="projectId != null and projectId.trim().length() != 0">AND PROJECT_ID = #{projectId}</if>
+			<if test="productId != null and productId.trim().length() != 0">AND PRODUCT_ID = #{productId}</if>
+		</where>) M JOIN saas_group_product_detail N ON M.CONTRACT_ID = N.CONTRACT_ID
+		JOIN (SELECT <include refid="Base_Sql_Column"></include> FROM saas_product_page 
+		<where>
+			<if test="productId != null and productId.trim().length() != 0">AND PRODUCT_ID = #{productId}</if>
+			<choose>
+                <when test="angle != null and angle == '1'">AND PAGE_ANGLE IN ('0', '1')</when>
+                <when test="angle != null and angle == '2'">AND PAGE_ANGLE IN ('0', '2')</when>
+            </choose>
+		</where>) P ON P.ID = N.PAGE_ID
+	</select>
+
+	<select id="queryAccountProductPages" resultMap="BaseResultMap">
+		SELECT * FROM (SELECT <include refid="Base_Sql_Column"></include> FROM saas_product_page 
+		<where>
+			<if test="productId != null and productId.trim().length() != 0">AND PRODUCT_ID = #{productId}</if>
+			<if test="parentId != null and parentId.trim().length() != 0">AND PARENT_ID = #{parentId}</if>
+			<choose>
+                <when test="angle != null and angle == '1'">AND PAGE_ANGLE IN ('0', '1')</when>
+                <when test="angle != null and angle == '2'">AND PAGE_ANGLE IN ('0', '2')</when>
+            </choose>
+		</where>) M
+		<!-- JOIN 内容为权限过滤数据 -->
+		JOIN (SELECT DISTINCT Q.PAGE_ID FROM (SELECT ROLE_ID FROM saas_account_role 
+		<where>
+			<if test="accountId != null and accountId.trim().length() != 0">AND ACCOUNT_ID = #{accountId}</if>
+		</where>) P
+		JOIN (SELECT ROLE_ID, PAGE_ID FROM saas_role_menu 
+		<where>
+			<if test="menuId != null and menuId.trim().length() != 0">AND MENU_ID = #{menuId}</if>
+		</where>) Q ON P.ROLE_ID = Q.ROLE_ID) N ON M.ID = N.PAGE_ID
+	</select>
+		
+</mapper>

+ 42 - 0
saas-account/src/main/resources/mapper/SaasProjectProductMapper.xml

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="com.persagy.account.mapper.SaasProjectProductMapper">
+    
+	<resultMap id="BaseResultMap" type="com.persagy.account.pojo.dto.SaasProjectProduct">
+		<result column="CONTRACT_ID" property="contractId" jdbcType="VARCHAR" />
+		<result column="GROUP_CODE" property="groupCode" jdbcType="VARCHAR" />
+		<result column="PROJECT_ID" property="projectId" jdbcType="VARCHAR" />
+		<result column="PRODUCT_ID" property="productId" jdbcType="VARCHAR" />
+		<result column="PRODUCT_CONTRACT" property="productContract" jdbcType="DATE" />
+		<result column="REMARK" property="remark" jdbcType="VARCHAR" />
+		
+		<result column="PROJECT_NAME" property="projectName" jdbcType="VARCHAR" />
+	</resultMap>
+	
+	<sql id="Base_Sql_Column">
+		CONTRACT_ID, GROUP_CODE, PROJECT_ID, PRODUCT_ID, PRODUCT_CONTRACT, REMARK
+	</sql>
+	
+	<select id="querySaasProjectProductList">
+		SELECT M.*, N.PROJECT_NAME FROM (SELECT <include refid="Base_Sql_Column"></include> FROM saas_project_product 
+		<where>
+			<if test="contractId != null and contractId.trim().length() != 0">AND CONTRACT_ID = #{contractId}</if>
+			<if test="groupCode != null and groupCode.trim().length() != 0">AND GROUP_CODE = #{groupCode}</if>
+			<if test="projectId != null and projectId.trim().length() != 0">AND PROJECT_ID = #{projectId}</if>
+			<if test="productId != null and productId.trim().length() != 0">AND PRODUCT_ID = #{productId}</if>
+		</where>) M
+		JOIN (SELECT PROJECT_ID, PROJECT_NAME FROM saas_project WHERE VALID = 1 
+			<if test="groupCode != null and groupCode.trim().length() != 0">AND GROUP_CODE = #{groupCode}</if>
+			<if test="projectId != null and projectId.trim().length() != 0">AND PROJECT_ID = #{projectId}</if>
+		) N ON M.PROJECT_ID = N.PROJECT_ID
+	</select>
+	
+	<insert id="batchInsertSaasProjectProduct">
+		INSERT INTO saas_project_product (CONTRACT_ID, GROUP_CODE, PROJECT_ID, PRODUCT_ID, PRODUCT_CONTRACT) VALUES
+		<foreach collection="projectIds" index="index" item="projectId" separator="," >
+			(#{contractId}, #{groupCode}, #{projectId}, #{productId}, NOW())
+		</foreach>
+	</insert>
+	
+</mapper>

+ 1 - 0
saas-account/src/main/resources/mapper/SaasRoleMenuMapper.xml

@@ -7,6 +7,7 @@
 		<result column="GROUP_CODE" property="groupCode" jdbcType="VARCHAR" />
 		<result column="GROUP_CODE" property="groupCode" jdbcType="VARCHAR" />
 		<result column="ROLE_ID" property="roleId" jdbcType="VARCHAR" />
 		<result column="ROLE_ID" property="roleId" jdbcType="VARCHAR" />
 		<result column="MENU_ID" property="menuId" jdbcType="VARCHAR" />
 		<result column="MENU_ID" property="menuId" jdbcType="VARCHAR" />
+		<result column="PAGE_ID" property="pageId" jdbcType="VARCHAR" />
 		<result column="FUNCTION_ID" property="functionId" jdbcType="VARCHAR" />
 		<result column="FUNCTION_ID" property="functionId" jdbcType="VARCHAR" />
 	</resultMap>
 	</resultMap>
 	
 	

+ 7 - 0
saas-common/src/main/java/com/persagy/common/constant/SaasCommonConstant.java

@@ -56,6 +56,12 @@ public interface SaasCommonConstant {
 	//产品ID前缀
 	//产品ID前缀
 	String SAAS_PRODUCT_ID_PREFIX = "PD";
 	String SAAS_PRODUCT_ID_PREFIX = "PD";
 	
 	
+	//产品页面ID前缀
+	String SAAS_PRODUCT_PAGE_ID_PREFIX = "PA";
+	
+	//产品签约ID前缀
+	String SAAS_GROUP_PRODUCT_ID_PREFIX = "GP";
+	
 	//项目ID前缀
 	//项目ID前缀
 	String SAAS_PROJECT_ID_PREFIX = "PR";
 	String SAAS_PROJECT_ID_PREFIX = "PR";
 	
 	
@@ -70,6 +76,7 @@ public interface SaasCommonConstant {
 	String APP_ID_REDIS_HASH_KEY = "APP_ID";
 	String APP_ID_REDIS_HASH_KEY = "APP_ID";
 	String GROUP_CODE_REDIS_HASH_KEY = "GROUP_CODE";
 	String GROUP_CODE_REDIS_HASH_KEY = "GROUP_CODE";
 	String ACCOUNT_ID_REDIS_HASH_KEY = "ACCOUNT_ID";
 	String ACCOUNT_ID_REDIS_HASH_KEY = "ACCOUNT_ID";
+	String INNER_CLIENT_REDIS_HASH_KEY = "INNER_CLIENT";
 	String REDIRECT_URL_REDIS_HASH_KEY = "REDIRECT_URL";
 	String REDIRECT_URL_REDIS_HASH_KEY = "REDIRECT_URL";
 	String REFRESH_TOKEN_REDIS_HASH_KEY = "REFRESH_TOKEN";
 	String REFRESH_TOKEN_REDIS_HASH_KEY = "REFRESH_TOKEN";
 	
 	

+ 27 - 0
saas-doc/doc/saas-platform_dict.txt

@@ -0,0 +1,27 @@
+appId:{
+	"PC":"电脑端", "APP":"手机应用端", "BIM":"BIM平台"
+}
+MENU_TYPE:{
+	"0":"系统菜单", "1":"业务菜单"
+}
+URL_TYPE:{
+	"0":"相对路径", "1":"绝对路径"
+}
+ACCOUNT_TYPE:{
+	"0":"超级管理员(所有集团)", "1":"单集团管理员(单集团)", "2":"普通账号"
+}
+ACCOUNT_BELONG:{
+	"0":"运维系统账号", "1":"业务账号"
+}
+VALID_LAST:{
+	"0":"期内有效", "1":"永久有效"
+}
+ACCOUNT_SOURCE:{
+	"100":"博锐尚格"
+}
+CAS_TYPE:{
+	"0":"主岗", "1":"副岗", "2":"业务超管默认角色"
+}
+AREA_TYPE:{
+	"0":"非最底层区域", "1":"最底层区域"
+}

+ 239 - 18
saas-doc/doc/saas_platform.md

@@ -58,11 +58,13 @@ API接口直接调用时,需先请求获取OpenId(目前为账号ID)的接口
 
 
 	{
 	{
 		"appId":"PC",			//登录端,PC-电脑,APP-手机应用端,必填,作为sql条件
 		"appId":"PC",			//登录端,PC-电脑,APP-手机应用端,必填,作为sql条件
-	    "username":"zhangsan",   //账号登录名,必填
-	    "password":"123456",    //账号密码,必填
+	    "username":"zhangsan",  // 账号登录名,必填
+	    "password":"123456",    // 账号密码,必填
 	    "remember":false,		// 是否记住登录,true-记住,默认false,选填
 	    "remember":false,		// 是否记住登录,true-记住,默认false,选填
 	    "auths":false,			// 是否获取此账号对应的菜单权限树,true-获取,默认false,选填
 	    "auths":false,			// 是否获取此账号对应的菜单权限树,true-获取,默认false,选填
-	    "redirectUrl":"http://127.0.0.1:8080/test"	//登录成功后的重定向链接,选填;当为空时,正常登陆,否则重定向到此地址
+	    "redirectUrl":"https://www.baidu.com",	//登录成功后的重定向链接,选填;当为空时,正常登陆,否则重定向到此地址(会经过URLEncoder编码)
+	    "clientId":"123",		// 客户端ID,选填
+	    "state":"456"			// 客户端自定义状态信息,选填
 	}
 	}
 
 
 返回:
 返回:
@@ -1149,7 +1151,8 @@ Header:Content-Type=application/json
 	{
 	{
 		"accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号,选填
 		"accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号,选填
 	    "groupCode":"TEST",    //集团编码,选填
 	    "groupCode":"TEST",    //集团编码,选填
-	    "accountId":"1371370289309515777",     //当前账号ID,选填
+	    "accountId":"1371370289309515777",     //当前账号ID,选填,会作为sql条件
+	    "accountIds":["1371370289309515777"],	//当前账号ID集合,选填,会作为sql条件
 	    "accountType":"0",          //账号类型, 0-超级管理员(所有集团),1-单集团管理员(单集团),2-普通账号,选填
 	    "accountType":"0",          //账号类型, 0-超级管理员(所有集团),1-单集团管理员(单集团),2-普通账号,选填
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,选填
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,选填
 	    "username":"zhangsan",     //登录用户名,支持模糊查询,选填
 	    "username":"zhangsan",     //登录用户名,支持模糊查询,选填
@@ -1165,7 +1168,9 @@ Header:Content-Type=application/json
 	        	"accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号
 	        	"accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号
 	            "accountSource": "persagy",
 	            "accountSource": "persagy",
 	            "accountType": "0",
 	            "accountType": "0",
-	            "appId": "PC",
+	            "terminal": [
+	                "PC"
+	            ],
 	            "createTime": 1615795130000,
 	            "createTime": 1615795130000,
 	            "validEndTime": 1899791706000,
 	            "validEndTime": 1899791706000,
 	            "groupCode": "TEST",
 	            "groupCode": "TEST",
@@ -1208,7 +1213,56 @@ Header:Content-Type=application/json
 	        	"accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号
 	        	"accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号
 	            "accountSource": "persagy",
 	            "accountSource": "persagy",
 	            "accountType": "0",
 	            "accountType": "0",
-	            "appId": "PC",
+	            "terminal": [
+	                "PC"
+	            ],
+	            "createTime": 1615795130000,
+	            "validEndTime": 1899791706000,
+	            "groupCode": "TEST",
+	            "headPortrait": "http://127.0.0.1:8080/test.img",
+	            "id": "1371370289309515777",
+	            "mail": "18888888888@163.com",
+	            "password": "123456",
+	            "phoneNum": "18888888888",
+	            "remark": "remark",
+	            "updateTime": 1615795130000,
+	            "username": "zhangsan",
+	            "valid": 1,
+	            "validStartTime": 1615720476000
+	        }
+	    ],
+	    "count": 1,
+	    "respCode": "00000",
+	    "respMsg": "success"
+	}
+
+### 分页查询角色账号信息
+
+	URL:http://develop.persagy.com/saas-platform/saas-account/account/queryRoleAccountPage
+	Method:POST
+	Header:Content-Type=application/json
+
+请求:
+
+	{
+	    "groupCode":"WD",    //集团编码,必填
+	    "roleIds":["RO1372505156457959327"],          //角色ID,必填
+	    "valid":1,         //是否可用,0-不可用,1-可用,选填
+	    "page":1,       //分页页码,不可低于1,必填
+	    "size":10       //分页大小,不可低于1,必填
+	}
+
+返回:
+
+	{
+	    "content": [
+	        {
+	        	"accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号
+	            "accountSource": "persagy",
+	            "accountType": "0",
+	            "terminal": [
+	                "PC"
+	            ],
 	            "createTime": 1615795130000,
 	            "createTime": 1615795130000,
 	            "validEndTime": 1899791706000,
 	            "validEndTime": 1899791706000,
 	            "groupCode": "TEST",
 	            "groupCode": "TEST",
@@ -1241,6 +1295,7 @@ Header:Content-Type=application/json
 		"accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号,选填
 		"accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号,选填
 	    "groupCode":"sybs",    //集团编码,选填
 	    "groupCode":"sybs",    //集团编码,选填
 	    "accountId":"AC1377819021444931585",     //当前账号ID,选填,会作为sql条件
 	    "accountId":"AC1377819021444931585",     //当前账号ID,选填,会作为sql条件
+	    "accountIds":["AC1377819021444931585"],	//当前账号ID集合,选填,会作为sql条件
 	    "accountType":"0",          //账号类型, 0-超级管理员(所有集团),1-单集团管理员(单集团),2-普通账号,选填
 	    "accountType":"0",          //账号类型, 0-超级管理员(所有集团),1-单集团管理员(单集团),2-普通账号,选填
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,选填
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,选填
 	    "username":"sybs",     //登录用户名,支持模糊查询,选填
 	    "username":"sybs",     //登录用户名,支持模糊查询,选填
@@ -1260,7 +1315,9 @@ Header:Content-Type=application/json
 	            "accountBelong": "1",
 	            "accountBelong": "1",
 	            "accountSource": "persagy",
 	            "accountSource": "persagy",
 	            "accountType": "1",
 	            "accountType": "1",
-	            "appId": "PC",
+	            "terminal": [
+	                "PC"
+	            ],
 	            "createTime": 1617292800000,
 	            "createTime": 1617292800000,
 	            "groupCode": "sybs",
 	            "groupCode": "sybs",
 	            "groupName": "啥也不是",
 	            "groupName": "啥也不是",
@@ -1314,7 +1371,9 @@ Header:Content-Type=application/json
 	        	"accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号
 	        	"accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号
 	            "accountSource": "persagy",
 	            "accountSource": "persagy",
 	            "accountType": "0",
 	            "accountType": "0",
-	            "appId": "PC",
+	            "terminal": [
+	                "PC"
+	            ],
 	            "createTime": 1615795130000,
 	            "createTime": 1615795130000,
 	            "validEndTime": 1899791706000,
 	            "validEndTime": 1899791706000,
 	            "groupCode": "TEST",
 	            "groupCode": "TEST",
@@ -1664,6 +1723,146 @@ Header:Content-Type=application/json
 	    "respMsg": "更新成功"
 	    "respMsg": "更新成功"
 	}
 	}
 
 
+### 查询产品页面树
+
+	URL:http://develop.persagy.com/saas-platform/saas-account/productPage/querySaasProductPageTree
+	Method:POST
+	Header:Content-Type=application/json
+
+请求:
+
+	{
+	    "accountId":"AC1372371635352403969",     // 账号ID,必填,不做为sql条件
+	    "appId":"PC",                   // 应用ID,PC-电脑,APP-手机应用,必填
+	    "productId":"PD1390234244466438145",        //产品ID,必填
+	    "showPageAuth": false   //是否展示出页面对应的权限信息,true-展示,默认false
+	}
+
+返回:
+
+	{
+	    "content": {
+	        "appId": "PC",
+	        "createTime": 1620292648000,
+	        "id": "PD1390234244466438145",
+	        "pages": [		// 该产品下,所有的页面
+	            {
+	                "childrens": [],		//该产品页面下的子页面
+	                "createTime": 1620292920000,
+	                "id": "PA1390235384671203330",
+	                "pageAngle": "0",		// 页面视角,0-集团项目页,1-集团独有页,2-项目独有页
+	                "pageFloor": "0",		// 是否为落地页,0-非落地页,1-集团落地页,2-项目落地页,3-集团项目落地页
+	                "pageIcon": "",
+	                "pageName": "测试页面",
+	                "pageUrl": "",
+	                "parentId": "",
+	                "productId": "PD1390234244466438145",
+	                "remark": "测试页面",
+	                "updateTime": 1620293119000
+	            }
+	        ],
+	        "productCode": "1002",		// 产品编码
+	        "productIcon": "layui-test-icon",
+	        "productMenu": "0",		// 产品是否关联菜单,0-未关联,1-已关联
+	        "productName": "测试2",
+	        "productState": "0",		// 产品状态,0-建设中,1-已完成,2-已关联
+	        "remark": "测试",
+	        "updateTime": 1620292648000,
+	        "updateUser": "AC68464684542455"
+	    },
+	    "respCode": "00000",
+	    "respMsg": "success"
+	}
+
+### 新增产品页面信息
+
+	URL:http://develop.persagy.com/saas-platform/saas-account/productPage/createSaasProductPage
+	Method:POST
+	Header:Content-Type=application/json
+
+请求:
+
+	{
+	    "accountId":"AC1372371635352403969",     // 账号ID,必填,不做为sql条件
+	    "appId":"PC",                   // 应用ID,PC-电脑,APP-手机应用,必填
+	    "productId":"PD1390234244466438145",        //产品ID,必填
+	    "parentId":"",           // 上级页面ID,选填
+	    "pageName":"测试页面",   // 页面名称,必填
+	    "pageIcon":"",         // 页面图标,选填
+	    "pageUrl":"",         // 页面URL,选填
+	    "pageAngle":"0",         // 页面视角,0-集团项目页,1-集团独有页,2-项目独有页,必填
+	    "pageFloor":"0",         // 是否为落地页,0-非落地页,1-集团落地页,2-项目落地页,3-集团项目落地页,默认0,不填-默认0
+	    "pageSort":1,           //页面列表展示上下顺序,必填
+	    "remark":"测试页面"             //备注,选填
+	}
+
+返回:
+
+	{
+	    "content": "PA1390235384671203330",
+	    "respCode": "00000",
+	    "respMsg": "保存成功"
+	}
+
+### 更新产品页面信息
+
+	URL:http://develop.persagy.com/saas-platform/saas-account/productPage/updateSaasProductPage
+	Method:POST
+	Header:Content-Type=application/json
+
+请求:
+
+	{
+	    "accountId":"AC1372371635352403969",     // 账号ID,必填,不做为sql条件
+	    "appId":"PC",                   // 应用ID,PC-电脑,APP-手机应用,必填
+	    "id":"PA1390235384671203330",    //页面ID,必填
+	    "productId":"PD1390234244466438145",        //产品ID,必填
+	    "parentId":"",           // 上级页面ID,选填
+	    "pageName":"测试页面",   // 页面名称,必填
+	    "pageIcon":"",         // 页面图标,选填
+	    "pageUrl":"",         // 页面URL,选填
+	    "pageAngle":"0",         // 页面视角,0-集团项目页,1-集团独有页,2-项目独有页,必填
+	    "pageFloor":"0",         // 是否为落地页,0-非落地页,1-集团落地页,2-项目落地页,3-集团项目落地页,默认0,不填-默认0
+	    "pageSort":1,           //页面列表展示上下顺序,必填
+	    "remark":"测试页面"             //备注,选填
+	}
+
+返回:
+
+	{
+	    "respCode": "00000",
+	    "respMsg": "更新成功"
+	}
+
+### 删除产品页面信息
+
+说明:落地页不允许删除,且级联删除此页面的权限点信息
+
+	URL:http://develop.persagy.com/saas-platform/saas-account/productPage/deleteSaasProductPage
+	Method:POST
+	Header:Content-Type=application/json
+
+请求:
+
+	{
+	    "accountId":"AC68464684542455",     // 账号ID,必填,不做为sql条件
+	    "appId":"PC",                   // 应用ID,PC-电脑,APP-手机应用,必填
+	    "id":"PD1386991620691402753",        //产品ID,必填
+	    "productCode":"1001",           // 产品编码,必填,唯一
+	    "productName":"测试",           // 产品名称,必填,唯一
+	    "productIcon":"layui-test-icon",   // 产品图标,必填
+	    "productState":"0",         // 产品状态,0-建设中,1-已完成,2-已关联,必填
+	    "productMenu":"0",          // 产品是否关联菜单,0-未关联,1-已关联,必填
+	    "remark":"测试但是"             //备注,选填
+	}
+
+返回:
+
+	{
+	    "respCode": "00000",
+	    "respMsg": "更新成功"
+	}
+
 ## 角色管理
 ## 角色管理
 
 
 ### 查询角色信息(未做权限过滤,全局角色列表)
 ### 查询角色信息(未做权限过滤,全局角色列表)
@@ -1919,7 +2118,8 @@ Header:Content-Type=application/json
 	    "accountId":"1371370289309515777",     //当前账号ID,必填,不做为sql条件
 	    "accountId":"1371370289309515777",     //当前账号ID,必填,不做为sql条件
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,必填,做为sql条件
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,必填,做为sql条件
 	    "menuId":"ME1372505574210637825",		//菜单ID,选填
 	    "menuId":"ME1372505574210637825",		//菜单ID,选填
-	    "menuType":"0"		//菜单类型,0-系统菜单,1-标准业务菜单,2-IBMS菜单,必填
+	    "menuType":"0",		//菜单类型,0-系统菜单,1-标准业务菜单,2-IBMS菜单,必填
+	    "isMenu":false			//是否仅查询出菜单树,true-是,默认false
 	}
 	}
 
 
 返回:
 返回:
@@ -2022,9 +2222,9 @@ Header:Content-Type=application/json
 		"accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号,必填
 		"accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号,必填
 	    "accountId":"1371370289309515777",     //当前账号ID,必填
 	    "accountId":"1371370289309515777",     //当前账号ID,必填
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,必填
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,必填
-	    "isMenu":false,		//是否仅查询出菜单树,true-是,默认false
+	    "isMenu":false,		// 是否仅查询出菜单树,true-是,默认false
+	    "angle":"1",		// 当前角度,不填,默认全查, 1-集团视角,2-项目视角
 	    "groupCode":"TEST"    //集团编码,选填
 	    "groupCode":"TEST"    //集团编码,选填
-	    
 	}
 	}
 
 
 返回:
 返回:
@@ -2161,6 +2361,7 @@ Header:Content-Type=application/json
 	    "accountId":"1371370289309515777",     //当前账号ID,必填
 	    "accountId":"1371370289309515777",     //当前账号ID,必填
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,必填
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,必填
 	    "parentId":"",    //上级菜单ID,选填
 	    "parentId":"",    //上级菜单ID,选填
+	    "productId":"PD1371370289309515257", // 产品ID,选填
 	    "menuCode":"1001",     //菜单编码,必填
 	    "menuCode":"1001",     //菜单编码,必填
 	    "menuName":"一级菜单",         //菜单名称,必填
 	    "menuName":"一级菜单",         //菜单名称,必填
 	    "menuUrl":"/query/list",    //菜单URL,选填
 	    "menuUrl":"/query/list",    //菜单URL,选填
@@ -2193,6 +2394,7 @@ Header:Content-Type=application/json
 	    "accountId":"1371370289309515777",     //当前账号ID,必填
 	    "accountId":"1371370289309515777",     //当前账号ID,必填
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,必填
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,必填
 	    "parentId":"",    //上级菜单ID,选填
 	    "parentId":"",    //上级菜单ID,选填
+	    "productId":"PD1371370289309515257", // 产品ID,选填
 	    "menuCode":"1001",     //菜单编码,必填
 	    "menuCode":"1001",     //菜单编码,必填
 	    "menuName":"一级菜单",         //菜单名称,必填
 	    "menuName":"一级菜单",         //菜单名称,必填
 	    "menuUrl":"/query/list",    //菜单URL,选填
 	    "menuUrl":"/query/list",    //菜单URL,选填
@@ -2225,7 +2427,7 @@ Header:Content-Type=application/json
 	    "accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号,必填
 	    "accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号,必填
 	    "accountId":"AC1372371635352403969",     //当前账号ID,必填
 	    "accountId":"AC1372371635352403969",     //当前账号ID,必填
 	    "appId":"PC",        //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,必填
 	    "appId":"PC",        //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,必填
-	    "groupCode":"TEST"      //集团编码
+	    "groupCode":"TEST"      //集团编码,必填
 	}
 	}
 
 
 返回:
 返回:
@@ -2249,16 +2451,32 @@ Header:Content-Type=application/json
 		"accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号,必填
 		"accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号,必填
 	    "accountId":"1371370289309515777",     //当前账号ID,必填,不做为sql条件
 	    "accountId":"1371370289309515777",     //当前账号ID,必填,不做为sql条件
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,必填,不做为sql条件
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,必填,不做为sql条件
-	    "menuId":"1371376123917754369",       //菜单ID,选填
-	    "funCode":"011001",    //功能编码,选填
-	    "funName":"测试功能点"       //功能名称,支持模糊查询,选填
+	    "menuId":"",       //菜单ID,选填
+	    "funCode":"1001",    //功能编码,选填
+	    "funName":"查询",       //功能名称,支持模糊查询,选填
+	    "pageId":"PA1390235384671203330"		//页面ID
 	}
 	}
 
 
 返回:
 返回:
 
 
 	{
 	{
+	    "content": [
+	        {
+	            "createTime": 1620300248000,
+	            "funCode": "1001",
+	            "funName": "查询",
+	            "funSort": 1,
+	            "funUrl": "/789/456",
+	            "id": "FU1390266121168408578",
+	            "menuId": "",
+	            "remark": "查询",
+	            "updateTime": 1620300362000,
+	            "updateUser": "AC1372371635352403969"
+	        }
+	    ],
+	    "count": 1,
 	    "respCode": "00000",
 	    "respCode": "00000",
-	    "respMsg": "删除成功"
+	    "respMsg": "success"
 	}
 	}
 
 
 ### 角色菜单功能点列表查询
 ### 角色菜单功能点列表查询
@@ -2315,7 +2533,8 @@ Header:Content-Type=application/json
 		"accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号,必填
 		"accountBelong":"0",		//账号所属, 0-运维系统账号,1-业务账号,必填
 	    "accountId":"1371370289309515777",     //当前账号ID,必填
 	    "accountId":"1371370289309515777",     //当前账号ID,必填
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,必填
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,必填
-	    "menuId":"1371376123917754369",       //菜单ID,必填
+	    "menuId":"1371376123917754369",       //菜单ID,与页面ID,二者必填其一
+	    "pageId":"",	//页面ID,与菜单ID,二者必填其一
 	    "funCode":"1001010102",    //功能编码,必填
 	    "funCode":"1001010102",    //功能编码,必填
 	    "funName":"删除",       //功能名称,必填
 	    "funName":"删除",       //功能名称,必填
 	    "funUrl":"/account/deleteSaasAccount",      //功能URL,必填
 	    "funUrl":"/account/deleteSaasAccount",      //功能URL,必填
@@ -2344,7 +2563,8 @@ Header:Content-Type=application/json
 	    "id":"FU1371635736550330370",        //功能ID,必填
 	    "id":"FU1371635736550330370",        //功能ID,必填
 	    "accountId":"1371370289309515777",     //当前账号ID,必填
 	    "accountId":"1371370289309515777",     //当前账号ID,必填
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,必填
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,必填
-	    "menuId":"1371376123917754369",       //菜单ID,必填
+	    "menuId":"1371376123917754369",       //菜单ID,与页面ID,二者必填其一
+	    "pageId":"",	//页面ID,与菜单ID,二者必填其一
 	    "funCode":"1001010102",    //功能编码,必填
 	    "funCode":"1001010102",    //功能编码,必填
 	    "funName":"删除",       //功能名称,必填
 	    "funName":"删除",       //功能名称,必填
 	    "funUrl":"/account/deleteSaasAccount",      //功能URL,必填
 	    "funUrl":"/account/deleteSaasAccount",      //功能URL,必填
@@ -2565,6 +2785,7 @@ Header:Content-Type=application/json
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,必填,不做为sql条件
 	    "appId":"PC",         //应用ID, PC-电脑端,APP-手机应用端,BIM-BIM平台,,必填,不做为sql条件
 	    "groupCode":"TEST",    //集团编码,选填
 	    "groupCode":"TEST",    //集团编码,选填
 	    "roleId":"RO1372505156457959425",      //角色ID,选填
 	    "roleId":"RO1372505156457959425",      //角色ID,选填
+	    "roleIds":["RO1372505156457959425"],      //角色ID集合,选填
 	    "accountIds":[		//账号ID集合,选填,返回结果是一条条的记录,而非按照账号ID聚合
 	    "accountIds":[		//账号ID集合,选填,返回结果是一条条的记录,而非按照账号ID聚合
 	    	"AC1372371635352403969"
 	    	"AC1372371635352403969"
 	    ]
 	    ]

BIN
saas-doc/doc/运维平台单点登录授权文档.docx


BIN
saas-doc/doc/运维管理3.0-数据库表设计.xlsx


+ 1 - 1
saas-gateway/src/main/resources/application.yml

@@ -2,7 +2,7 @@ integrated:
   security:
   security:
 #    token-enable: ${INTEGRATED_SECURITY_TOKEN_ENABLE:true}
 #    token-enable: ${INTEGRATED_SECURITY_TOKEN_ENABLE:true}
     ignore-url: >
     ignore-url: >
-      ${INTEGRATED_SECURITY_IGNORE_URL:login,remember}
+      ${INTEGRATED_SECURITY_IGNORE_URL:login,remember,authCenter/code,authCenter/codeToken}
 #saas:
 #saas:
 #  secure:
 #  secure:
 #    aes:
 #    aes: