Browse Source

产品配置BUG修改

zhangqiankun 3 years ago
parent
commit
8cc463f1e5
20 changed files with 133 additions and 52 deletions
  1. 4 4
      saas-account/src/main/java/com/persagy/account/config/ApplicationProperties.java
  2. 1 0
      saas-account/src/main/java/com/persagy/account/controller/SaasAccountController.java
  3. 1 1
      saas-account/src/main/java/com/persagy/account/controller/SaasAuthCenterController.java
  4. 2 1
      saas-account/src/main/java/com/persagy/account/controller/SaasFunctionController.java
  5. 23 7
      saas-account/src/main/java/com/persagy/account/controller/SaasLoginController.java
  6. 3 2
      saas-account/src/main/java/com/persagy/account/controller/SaasMenuController.java
  7. 2 1
      saas-account/src/main/java/com/persagy/account/controller/SaasProductController.java
  8. 1 1
      saas-account/src/main/java/com/persagy/account/manage/SaasAreaProjectHandler.java
  9. 8 8
      saas-account/src/main/java/com/persagy/account/manage/SaasAuthHandler.java
  10. 17 13
      saas-account/src/main/java/com/persagy/account/manage/SaasProductHandler.java
  11. 9 7
      saas-account/src/main/java/com/persagy/account/manage/SaasRoleMenuHandler.java
  12. 3 0
      saas-account/src/main/java/com/persagy/account/pojo/dto/SaasAccount.java
  13. 9 1
      saas-account/src/main/java/com/persagy/account/pojo/dto/SaasMenu.java
  14. 2 2
      saas-account/src/main/java/com/persagy/account/pojo/vo/auth/SaasRoleMenuQueryVO.java
  15. 7 0
      saas-account/src/main/java/com/persagy/account/pojo/vo/menu/SaasMenuCreateVO.java
  16. 7 0
      saas-account/src/main/java/com/persagy/account/service/ISaasProductService.java
  17. 22 0
      saas-account/src/main/java/com/persagy/account/service/impl/SaasProductServiceImpl.java
  18. 3 2
      saas-account/src/main/resources/mapper/SaasAccountMapper.xml
  19. 5 2
      saas-account/src/main/resources/mapper/SaasMenuMapper.xml
  20. 4 0
      saas-doc/doc/saas_platform.md

+ 4 - 4
saas-account/src/main/java/com/persagy/account/config/ApplicationProperties.java

@@ -36,11 +36,11 @@ public class ApplicationProperties {
 	@Value("${saas.rabitmq.account-route-key}")
 	private String accountRouteKey;
 	
-	@Value("${saas.secure.aes.key}")
-	private String aesKey;
+	@Value("${integrated.security.login-fail.enable:true}")
+	private boolean enableLoginFail;
 	
-	@Value("${saas.secure.aes.iv}")
-	private String aesIv;
+	@Value("${integrated.security.login-fail.fail-num:5}")
+	private Integer loginFailNum;
 	
 	@Value("${saas.user.token.expire}")
 	private String tokenExpire;

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

@@ -314,6 +314,7 @@ public class SaasAccountController {
 				.validEq(SaasCommonConstant.STATUS_0).builderUpdateWrapper();
     	updateWrapper.set(SaasAccount::getUpdateUser, model.getAccountId());
     	updateWrapper.set(SaasAccount::getValid, SaasCommonConstant.STATUS_1);
+    	updateWrapper.set(SaasAccount::getLoginFail, 0);
     	boolean result = this.saasAccountService.update(updateWrapper);
         return result ? ResponseResultUtil.successResult("启用成功") : ResponseResultUtil.errorResult("启用失败");
     }

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

@@ -114,7 +114,7 @@ public class SaasAuthCenterController {
     	this.redisTemplate.opsForHash().put(model.getClientId(), CipherConstants.REFRESH_TOKEN_HASH_KEY, refreshToken);
     	
     	// AES加密出账号信息所需字段,组合为token
-    	SecureAES aes = new SecureAES(properties.getAesKey(), properties.getAesIv());
+    	SecureAES aes = new SecureAES(bouncycastleCipher.getProperties().getKey(), bouncycastleCipher.getProperties().getIv());
     	String tokenPrefix = aes.encrypt(this.getTokenPrefix(model.getClientId(), accountId));
     	token = tokenPrefix + CipherConstants.POINT_JOIN_SYMBOL + token;
     	

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

@@ -86,6 +86,7 @@ public class SaasFunctionController {
     	Map<String, String> authMap = this.saasAuthHandler.validAccountAuth(model.getAccountId(), model.getGroupCode());
 		String groupCode = authMap.get(CipherConstants.GROUP_CODE);
 		String accountType = authMap.get(SaasCommonConstant.ACCOUNT_TYPE);
+		String accountBelong = authMap.get(SaasCommonConstant.ACCOUNT_BELONG);
 		if (SaasCommonConstant.STR_STATUS_0.equals(accountType)) {
 			isAdmin = true;		// 超管
 		}
@@ -103,7 +104,7 @@ public class SaasFunctionController {
 			return ResponseResultUtil.successResult(Lists.newArrayList());
 		}
     	// 获取此账号、此菜单下所对应的功能点信息集合 或者 产品页面树集合
-    	this.saasProductHandler.queryMenuProductAuths(saasMenu, model.getAccountId(), model.getProjectId(), groupCode, isAdmin, model.isShowPageAuth());
+    	this.saasProductHandler.queryMenuProductAuths(saasMenu, model.getAccountId(), accountBelong, model.getProjectId(), groupCode, isAdmin, model.isShowPageAuth());
         if (StringUtil.isBlank(saasMenu.getProductId())) {
         	return ResponseResultUtil.successResult(saasMenu.getFunctions());
 		} 

+ 23 - 7
saas-account/src/main/java/com/persagy/account/controller/SaasLoginController.java

@@ -91,7 +91,7 @@ public class SaasLoginController {
 	private RedisTemplate<String, Object> redisTemplate;
 	
     /**
-     * 运维系统账号登录
+     * 运维系统账号登录,这里的登录失败次数验证,若后续账号用户名,非全局唯一,这里需要改动
      * 
      * @throws NumberFormatException 
      * @throws IOException 
@@ -99,17 +99,32 @@ public class SaasLoginController {
     @ApiOperation(value = "运维系统账号登录")
     @RequestMapping(value = "saas/login", method = RequestMethod.POST)
     public ResponseResult saasAccountLogin(@RequestBody @Validated SaasAccountLoginVO loginVO, HttpServletRequest request, HttpServletResponse response) throws NumberFormatException, IOException {
+    	// 如果是单点登录,验证重定向URL
     	String redirectUrl = this.validSaasSSO(loginVO);
-    	
-    	LambdaQueryWrapper<SaasAccount> queryWrapper = new SaasAccount.Builder().createQueryWrapper().usernameEq(loginVO.getUsername()).passwordEq(loginVO.getPassword()).builderQueryWrapper();
+    	// 获取账号信息
+    	LambdaQueryWrapper<SaasAccount> queryWrapper = new SaasAccount.Builder().createQueryWrapper().usernameEq(loginVO.getUsername()).builderQueryWrapper();
     	List<SaasAccount> accounts = this.saasAccountService.list(queryWrapper);
     	if (CollectionUtil.isEmpty(accounts) || accounts.size() > 1) {
-    		return ResponseResultUtil.errorResult(ResponseCode.A0220.getCode(), "用户名/密码错误");
+    		return ResponseResultUtil.errorResult(ResponseCode.A0220.getCode(), "无效账号");
 		}
     	SaasAccount account = accounts.get(0);
     	// 验证此账号是否可用
     	if (SaasCommonConstant.STATUS_1 != account.getValid()) {
-    		return ResponseResultUtil.errorResult(ResponseCode.A0220.getCode(), "此账号已停用");
+    		return ResponseResultUtil.errorResult(ResponseCode.A0220.getCode(), "此账号已被冻结,请联系管理员解冻");
+		}
+    	// 验证密码
+    	if (!loginVO.getPassword().equals(account.getPassword())) {
+    		// 更新失败次数
+    		Integer loginFailNum = properties.getLoginFailNum() == null ? 0 : properties.getLoginFailNum();
+    		LambdaUpdateWrapper<SaasAccount> updateWrapper = new SaasAccount.Builder().createUpdateWrapper().idEq(account.getId()).builderUpdateWrapper();
+    		if (account.getLoginFail() >= loginFailNum) {
+    			updateWrapper.setSql("LOGIN_FAIL = LOGIN_FAIL + 1, VALID = 0");
+    			this.saasAccountService.update(updateWrapper);
+    			return ResponseResultUtil.errorResult(ResponseCode.A0220.getCode(), "此账号已被冻结,请联系管理员解冻");
+			}
+    		updateWrapper.setSql("LOGIN_FAIL = LOGIN_FAIL + 1");
+    		this.saasAccountService.update(updateWrapper);
+    		return ResponseResultUtil.errorResult(ResponseCode.A0220.getCode(), "用户名/密码错误");
 		}
     	/*if (!account.getTerminal().contains(loginVO.getAppId())) {
     		return ResponseResultUtil.errorResult(ResponseCode.A0220.getCode(), "此账号不允许在此端登录");
@@ -138,6 +153,7 @@ public class SaasLoginController {
     			.validEq(SaasCommonConstant.STATUS_1).builderUpdateWrapper();
     	updateWrapper.set(SaasAccount::getLastLoginTime, new Date());
     	updateWrapper.set(SaasAccount::getLastLoginIp, address);
+    	updateWrapper.set(SaasAccount::getLoginFail, 0);
     	boolean result = this.saasAccountService.update(updateWrapper);
     	if (!result) {
     		return ResponseResultUtil.errorResult("登录信息更新失败");
@@ -302,11 +318,11 @@ public class SaasLoginController {
      */
     private String setRedisToken(SaasAccount account, String loginApp, boolean remember, boolean isEqual) throws UnsupportedEncodingException {
     	// 生成token,并验证之前登录是否为记住登录
-    	SecureAES aes = new SecureAES(properties.getAesKey(), properties.getAesIv());
+    	SecureAES aes = new SecureAES(this.bouncycastleCipher.getProperties().getKey(), this.bouncycastleCipher.getProperties().getIv());
     	// 对称性加密用户信息
     	String data = aes.encryptAccount(this.tokenAccountInfo(account, loginApp, remember));
     	// token存在判断
-    	String sign = (String)this.redisTemplate.opsForValue().get(account.getId());
+    	String sign = (String) this.redisTemplate.opsForValue().get(account.getId());
     	if (sign != null) {
     		if (isEqual) {
 	    		// 判断是否与redis中一致,不一致,返回错误信息

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

@@ -22,6 +22,7 @@ import com.persagy.account.pojo.vo.menu.SaasMenuSortVO;
 import com.persagy.account.service.ISaasMenuService;
 import com.persagy.account.service.ISaasProductService;
 import com.persagy.account.service.ISaasRoleMenuService;
+import com.persagy.common.constant.SaasCommonConstant;
 import com.persagy.common.enums.ResponseCode;
 import com.persagy.common.utils.ResponseResult;
 import com.persagy.common.utils.ResponseResultUtil;
@@ -98,8 +99,8 @@ public class SaasMenuController {
 		if (CollectionUtil.isEmpty(topList)) {
 			return ResponseResultUtil.successResult(Lists.newArrayList());
 		}
-		// 2.转为菜单树
-		this.saasRoleMenuHandler.setMenuChildrens(null, queryVO.getGroupCode(), queryVO.getProjectId(), null, topList, true, !queryVO.isMenu());
+		// 2.转为菜单树,这里由于不做权限过滤,这里也不进行签约验证,直接走系统账号逻辑
+		this.saasRoleMenuHandler.setMenuChildrens(null, SaasCommonConstant.STR_STATUS_0, queryVO.getGroupCode(), queryVO.getProjectId(), null, topList, true, !queryVO.isMenu());
     	return ResponseResultUtil.successResult(topList);
     }
     

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

@@ -19,6 +19,7 @@ import com.persagy.account.pojo.vo.product.SaasProductCreateVO;
 import com.persagy.account.pojo.vo.product.SaasProductPageVO;
 import com.persagy.account.pojo.vo.product.SaasProductQueryVO;
 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;
@@ -144,7 +145,7 @@ public class SaasProductController {
 		SaasProduct saasProduct = new SaasProduct();
 		BeanUtils.copyProperties(model, saasProduct);
 		saasProduct.setUpdateUser(model.getAccountId());
-        result = this.saasProductService.updateById(saasProduct);
+        result = this.saasProductService.updateSaasProduct(saasProduct, SaasCommonConstant.STR_STATUS_1.equals(saasProduct.getProductMenu()));
         return result ? ResponseResultUtil.successResult("更新成功") : ResponseResultUtil.errorResult("更新失败");
     }
 

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

@@ -70,7 +70,7 @@ public class SaasAreaProjectHandler {
 	 */
 	public List<SaasArea> getTopAreaList(String groupCode) {
 		LambdaQueryWrapper<SaasArea> queryWrapper = new SaasArea.Builder().createQueryWrapper().groupCodeEq(groupCode).builderQueryWrapper();
-    	queryWrapper.last("AND (PARENT_ID IS NULL OR TRIM(PARENT_ID) = '')");
+    	queryWrapper.apply("(PARENT_ID IS NULL OR TRIM(PARENT_ID) = '')");
     	List<SaasArea> topList = this.saasAreaService.list(queryWrapper);
     	return CollectionUtil.isEmpty(topList) ? Lists.newArrayList() : topList;
 	}

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

@@ -108,14 +108,14 @@ public class SaasAuthHandler {
      */
     public List<SaasGroup> getAllowGroupList(String accountId, String groupCode, String accountType) {
     	List<SaasGroup> groups = null;
-    	if (SaasCommonConstant.STR_STATUS_0.equals(accountType)) {
-    		LambdaQueryWrapper<SaasGroup> queryWrapper = new SaasGroup.Builder().createQueryWrapper().validEq(SaasCommonConstant.STATUS_1).builderQueryWrapper();
-    		groups = this.saasGroupService.list(queryWrapper);
-		} else {
-			SaasGroup saasGroup = new SaasGroup();
+    	if (SaasCommonConstant.STR_STATUS_2.equals(accountType)) {
+    		SaasGroup saasGroup = new SaasGroup();
 			saasGroup.setGroupCode(groupCode);
 			saasGroup.setValid(SaasCommonConstant.STATUS_1);
 			groups = this.saasGroupService.queryAllowGroupInfo(saasGroup, accountId);
+		} else {
+			LambdaQueryWrapper<SaasGroup> queryWrapper = new SaasGroup.Builder().createQueryWrapper().validEq(SaasCommonConstant.STATUS_1).builderQueryWrapper();
+    		groups = this.saasGroupService.list(queryWrapper);
 		}
 		return groups == null ? Lists.newArrayList() : groups;
     }
@@ -145,11 +145,11 @@ public class SaasAuthHandler {
      */
     public List<SaasArea> getAllowAreaList(String accountId, String groupCode, String accountType) {
     	List<SaasArea> topList = this.saasAreaProjectHandler.getTopAreaList(groupCode);
-		if (SaasCommonConstant.STR_STATUS_0.equals(accountType)) {
-			this.saasAreaProjectHandler.querySaasAreaTree(topList, false);
-		} else {
+		if (SaasCommonConstant.STR_STATUS_2.equals(accountType)) {
 			// 其他类型时,需要根据权限表数据,去匹配区域树
 			this.saasAreaProjectHandler.queryAllowAreaTree(topList, accountId, groupCode);
+		} else {
+			this.saasAreaProjectHandler.querySaasAreaTree(topList, false);
 		}
 		return topList;
     }

+ 17 - 13
saas-account/src/main/java/com/persagy/account/manage/SaasProductHandler.java

@@ -57,12 +57,13 @@ public class SaasProductHandler {
 	 * 
 	 * @param saasMenu 不可为空
 	 * @param accountId
+	 * @param accountBelong 系统账号不判断视角,也不判断是否签约该产品
 	 * @param projectId
 	 * @param groupCode
 	 * @param isAdmin
 	 * @param showFunctions
 	 */
-	public void queryMenuProductAuths(SaasMenu saasMenu, String accountId, String projectId, String groupCode, boolean isAdmin, boolean showFunctions) {
+	public void queryMenuProductAuths(SaasMenu saasMenu, String accountId, String accountBelong, String projectId, String groupCode, boolean isAdmin, boolean showFunctions) {
 		// angle 1-集团视角,2-项目视角
 		String angle = StringUtil.isBlank(projectId) ? SaasCommonConstant.STR_STATUS_1 : SaasCommonConstant.STR_STATUS_2;
 		if (StringUtil.isBlank(saasMenu.getProductId())) {
@@ -77,18 +78,21 @@ public class SaasProductHandler {
 			saasMenu.setFunctions(CollectionUtil.isEmpty(functions) ? Lists.newArrayList() : 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;	// 未签约
+		// 挂有产品,并判断是否是系统账号
+		if (!SaasCommonConstant.STR_STATUS_0.equals(accountBelong)) {
+			// 非系统账号,验证是否以签约改产品
+			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, showFunctions);

+ 9 - 7
saas-account/src/main/java/com/persagy/account/manage/SaasRoleMenuHandler.java

@@ -90,9 +90,9 @@ public class SaasRoleMenuHandler {
     	List<SaasMenu> menuTree = null;
     	if (SaasCommonConstant.STR_STATUS_0.equals(accountType) && SaasCommonConstant.STR_STATUS_0.equals(accountBelong)) {
     		// 如果是运维系统的账号,accountType为0的情况下查询所有菜单权限,其余情况走表关联查询,为1时,后续可添加
-    		menuTree = this.querySaasMenuTree(null, null, queryVO.getProjectId(), queryVO.getAppId(), true, queryVO.isMenu());
+    		menuTree = this.querySaasMenuTree(null, accountBelong, null, queryVO.getProjectId(), queryVO.getAppId(), true, queryVO.isMenu());
     	} else {
-    		menuTree = this.querySaasMenuTree(queryVO.getAccountId(), queryVO.getGroupCode(), queryVO.getProjectId(), queryVO.getAppId(), false, queryVO.isMenu());
+    		menuTree = this.querySaasMenuTree(queryVO.getAccountId(), accountBelong, queryVO.getGroupCode(), queryVO.getProjectId(), queryVO.getAppId(), false, queryVO.isMenu());
 		}
     	return menuTree;
 	}
@@ -101,6 +101,7 @@ public class SaasRoleMenuHandler {
 	 * 查询账号菜单功能权限树,菜单定义和角色定义编码相同,所以这里应该同步传递
 	 * 
 	 * @param accountId
+	 * @param accountBelong
 	 * @param groupCode
 	 * @param projectId
 	 * @param appId
@@ -108,7 +109,7 @@ public class SaasRoleMenuHandler {
 	 * @param isMenu 是否仅查询出菜单树,true-是
 	 * @return 空集合或数据菜单树
 	 */
-	public List<SaasMenu> querySaasMenuTree(String accountId, String groupCode, String projectId, String appId, boolean isAdmin, boolean isMenu) {
+	public List<SaasMenu> querySaasMenuTree(String accountId, String accountBelong, String groupCode, String projectId, String appId, boolean isAdmin, boolean isMenu) {
 		// 1.获取此账号对应菜单集合
 		List<SaasMenu> menuTree = null;
 		if (isAdmin) {
@@ -130,13 +131,14 @@ public class SaasRoleMenuHandler {
 			}
 		}
 		List<SaasMenu> parents = topMenus.stream().sorted(Comparator.comparing(SaasMenu::getMenuSort)).collect(Collectors.toList());
-		this.setMenuChildrens(accountId, groupCode, projectId, menuTree, parents, isAdmin, !isMenu);
+		this.setMenuChildrens(accountId, accountBelong, groupCode, projectId, menuTree, parents, isAdmin, !isMenu);
 		return parents;
 	}
 
 	/**
 	 * 生成菜单树
 	 * @param accountId 为null时,isAdmin 为true
+	 * @param accountBelong
 	 * @param angle 1-集团视角,2-项目视角
 	 * @param groupCode
 	 * @param menuTree 为空时,会去查询,但是不会做权限过滤
@@ -144,13 +146,13 @@ public class SaasRoleMenuHandler {
 	 * @param isAdmin 是否为超管 true-是
 	 * @param menuAndFun 
 	 */
-	public void setMenuChildrens(String accountId, String groupCode, String projectId, List<SaasMenu> menuTree, List<SaasMenu> parents, boolean isAdmin, boolean menuAndFun) {
+	public void setMenuChildrens(String accountId, String accountBelong, String groupCode, String projectId, List<SaasMenu> menuTree, List<SaasMenu> parents, boolean isAdmin, boolean menuAndFun) {
 		for (SaasMenu parent : parents) {
 			if (StringUtil.isBlank(parent.getMenuId())) {
 				parent.setMenuId(parent.getId());
 			}
 			if (StringUtil.isNotBlank(parent.getMenuUrl())) {
-				this.saasProductHandler.queryMenuProductAuths(parent, accountId, groupCode, projectId, isAdmin, menuAndFun);
+				this.saasProductHandler.queryMenuProductAuths(parent, accountId, accountBelong, groupCode, projectId, isAdmin, menuAndFun);
 			}
 			
 			List<SaasMenu> childrens = null;
@@ -170,7 +172,7 @@ public class SaasRoleMenuHandler {
 				}
 			}
 			parent.setChildrens(childrens);
-			this.setMenuChildrens(accountId, groupCode, projectId, menuTree, childrens, isAdmin, menuAndFun);
+			this.setMenuChildrens(accountId, accountBelong, groupCode, projectId, menuTree, childrens, isAdmin, menuAndFun);
 		}
 	}
 	

+ 3 - 0
saas-account/src/main/java/com/persagy/account/pojo/dto/SaasAccount.java

@@ -77,6 +77,9 @@ public class SaasAccount extends BaseEntity<SaasAccount> {
 
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	private String lastLoginIp; // 账号上一次登录IP
+	
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private Integer loginFail; 	// 账号登录失败次数,超过阈值,冻结,阈值来源于配置中心的 integrated.security.login-fail.fail-num 属性
 
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	private Date validStartTime; // 账号有效期的开始时间

+ 9 - 1
saas-account/src/main/java/com/persagy/account/pojo/dto/SaasMenu.java

@@ -3,12 +3,14 @@ package com.persagy.account.pojo.dto;
 import java.util.Date;
 import java.util.List;
 
+import com.alibaba.fastjson.JSONObject;
 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.handlers.FastjsonTypeHandler;
 import com.persagy.common.annotation.IdGeneratorPrefix;
 import com.persagy.common.constant.SaasCommonConstant;
 import com.persagy.common.model.BaseEntity;
@@ -31,7 +33,7 @@ import lombok.ToString;
 @Setter
 @ToString
 @EqualsAndHashCode(callSuper = false)
-@TableName("saas_menu")
+@TableName(value = "saas_menu", autoResultMap = true)
 @IdGeneratorPrefix(SaasCommonConstant.SAAS_MENU_ID_PREFIX)
 public class SaasMenu extends BaseEntity<SaasMenu> {
 	private static final long serialVersionUID = -4911716291586261567L;
@@ -65,6 +67,12 @@ public class SaasMenu extends BaseEntity<SaasMenu> {
 	
 	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
 	private String menuType;	// 菜单类型,0-系统菜单,1-业务菜单
+	
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
+	private String menuGroup;	// 分组菜单,0-非分组菜单,1-分组菜单
+	
+	@TableField(updateStrategy = FieldStrategy.NOT_EMPTY, typeHandler = FastjsonTypeHandler.class)
+	private JSONObject extraMenu;	// 菜单额外字段
 
 	@TableField(fill = FieldFill.INSERT, updateStrategy = FieldStrategy.NOT_EMPTY)
 	private Date createTime; // 创建时间

+ 2 - 2
saas-account/src/main/java/com/persagy/account/pojo/vo/auth/SaasRoleMenuQueryVO.java

@@ -1,6 +1,6 @@
 package com.persagy.account.pojo.vo.auth;
 
-import com.persagy.common.model.BaseGroupModel;
+import com.persagy.account.pojo.vo.BaseGroupVO;
 
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
@@ -20,7 +20,7 @@ import lombok.ToString;
 @Setter
 @ApiModel
 @ToString
-public class SaasRoleMenuQueryVO extends BaseGroupModel {
+public class SaasRoleMenuQueryVO extends BaseGroupVO {
 
 	@ApiModelProperty(value = "角色ID", example = "123")
 	private String roleId; // 角色ID

+ 7 - 0
saas-account/src/main/java/com/persagy/account/pojo/vo/menu/SaasMenuCreateVO.java

@@ -4,6 +4,7 @@ import javax.validation.constraints.Min;
 import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
 
+import com.alibaba.fastjson.JSONObject;
 import com.persagy.common.model.BaseUserModel;
 
 import io.swagger.annotations.ApiModel;
@@ -62,6 +63,12 @@ public class SaasMenuCreateVO extends BaseUserModel {
 	@ApiModelProperty(value = "菜单类型,0-系统菜单,1-业务菜单", example = "0")
 	private String menuType;	//菜单类型,0-系统菜单,1-业务菜单
 	
+	@ApiModelProperty(value = "分组菜单,0-非分组菜单,1-分组菜单", example = "0")
+	private String menuGroup;	// 分组菜单,0-非分组菜单,1-分组菜单
+	
+	@ApiModelProperty(value = "菜单额外字段,json格式", example = "0")
+	private JSONObject extraMenu;	// 菜单额外字段
+	
 	@ApiModelProperty(value = "备注", example = "测试菜单")
 	private String remark;   // 备注
 

+ 7 - 0
saas-account/src/main/java/com/persagy/account/service/ISaasProductService.java

@@ -14,6 +14,13 @@ import com.persagy.account.pojo.dto.SaasProduct;
 public interface ISaasProductService extends ISuperService<SaasProduct> {
 
 	/**
+	 * 更新产品信息
+	 * @param saasProduct
+	 * @return
+	 */
+	boolean updateSaasProduct(SaasProduct saasProduct, boolean isUpdateMenu);
+	
+	/**
 	 * 验证产品编码的唯一性
 	 * @param productType
 	 * @param productCode

+ 22 - 0
saas-account/src/main/java/com/persagy/account/service/impl/SaasProductServiceImpl.java

@@ -1,11 +1,17 @@
 package com.persagy.account.service.impl;
 
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import com.persagy.common.service.impl.SuperServiceImpl;
 import com.persagy.common.utils.StringUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
+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.SaasProduct;
 import com.persagy.account.service.ISaasProductService;
 
@@ -20,6 +26,22 @@ import com.persagy.account.service.ISaasProductService;
 @Service
 public class SaasProductServiceImpl extends SuperServiceImpl<SaasProductMapper, SaasProduct> implements ISaasProductService {
 
+	@Autowired
+	private SaasMenuMapper saasMenuMapper;
+	
+	@Override
+	@Transactional
+	public boolean updateSaasProduct(SaasProduct saasProduct, boolean isUpdateMenu) {
+		Integer result = this.baseMapper.updateById(saasProduct);
+		if (SqlHelper.retBool(result) && isUpdateMenu) {
+			// 更新菜单图标为此产品的图标
+			LambdaUpdateWrapper<SaasMenu> updateWrapper = new SaasMenu.Builder().createUpdateWrapper().productId(saasProduct.getId()).builderUpdateWrapper();
+			updateWrapper.set(SaasMenu::getMenuIcon, saasProduct.getProductIcon());
+			this.saasMenuMapper.update(null, updateWrapper);
+		}
+		return SqlHelper.retBool(result);
+	}
+	
 	@Override
 	public boolean validProductCode(String productType, String productCode, String id) {
 		LambdaQueryWrapper<SaasProduct> queryWrapper = new SaasProduct.BuilderQueryWrapper().productTypeEq(productType).productCodeEq(productCode).builder();

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

@@ -18,6 +18,7 @@
 		<result column="ACCOUNT_SOURCE" property="accountSource" jdbcType="VARCHAR" />
 		<result column="LAST_LOGIN_TIME" property="lastLoginTime" jdbcType="DATE" />
 		<result column="LAST_LOGIN_IP" property="lastLoginIp" jdbcType="VARCHAR" />
+		<result column="LOGIN_FAIL" property="loginFail" jdbcType="TINYINT" />
 		<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" />
@@ -29,10 +30,10 @@
 		
 		<result column="GROUP_NAME" property="groupName" jdbcType="VARCHAR" />
 	</resultMap>
-	
+
 	<sql id="Base_Sql_Columns">
 		ID, GROUP_CODE, TERMINAL, USERNAME, PHONE_NUM, MAIL, HEAD_PORTRAIT, ACCOUNT_TYPE, ACCOUNT_BELONG, ACCOUNT_SOURCE, LAST_LOGIN_TIME,
-		LAST_LOGIN_IP, VALID_START_TIME, VALID_END_TIME, VALID_LAST, CREATE_TIME, UPDATE_TIME, UPDATE_USER, REMARK, VALID
+		LAST_LOGIN_IP, VALID_START_TIME, VALID_END_TIME, VALID_LAST, CREATE_TIME, UPDATE_TIME, UPDATE_USER, REMARK, VALID, LOGIN_FAIL
 	</sql>
 	
 	<!-- 账号信息查询,关联出集团名称,这里必须 LEFT JOIN -->

+ 5 - 2
saas-account/src/main/resources/mapper/SaasMenuMapper.xml

@@ -14,7 +14,10 @@
 		<result column="URL_TYPE" property="urlType" jdbcType="VARCHAR" />
 		<result column="MENU_ICON" property="menuIcon" jdbcType="VARCHAR" />
 		<result column="MENU_SORT" property="menuSort" jdbcType="INTEGER" />
+		<result column="MENU_GROUP" property="menuGroup" jdbcType="VARCHAR" />
 		<result column="MENU_TYPE" property="menuType" jdbcType="VARCHAR" />
+		<result column="EXTRA_MENU" property="extraMenu" 
+			typeHandler="com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler"/>
 		<result column="CREATE_TIME" property="createTime" jdbcType="DATE" />
 		<result column="UPDATE_TIME" property="updateTime" jdbcType="DATE" />
 		<result column="UPDATE_USER" property="updateUser" jdbcType="VARCHAR" />
@@ -27,7 +30,7 @@
 	</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
+		ID, PARENT_ID, APP_ID, PRODUCT_ID, MENU_CODE, MENU_NAME, MENU_URL, URL_TYPE, MENU_ICON, MENU_SORT, MENU_TYPE, MENU_GROUP, EXTRA_MENU, REMARK
 	</sql>
 	
 	<!-- 查询所被允许的账号菜单权限 -->
@@ -58,7 +61,7 @@
 	<!-- 查询出此角色下,所有不存在功能点的菜单集合 -->
 	<select id="queryMenuAndEmptyFunction" resultMap="BaseResultMap">
 		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.MENU_GROUP, N.EXTRA_MENU, N.REMARK FROM (
 			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="roleId != null and roleId.trim().length() != 0">AND ROLE_ID = #{roleId}</if>

+ 4 - 0
saas-doc/doc/saas_platform.md

@@ -2878,10 +2878,12 @@ Header:Content-Type=application/json
 	    "menuCode":"1001",     //菜单编码,必填
 	    "menuName":"一级菜单",         //菜单名称,必填
 	    "menuUrl":"/query/list",    //菜单URL,选填
+	    "menuType":"0",   		 //分组菜单,0-非分组菜单,1-分组菜单,默认0,选填
 	    "urlType":"0",	//URL类型,0-相对路径,1-绝对路径,选填,当 menuUrl 不为空时,此处必填
 	    "menuIcon":"layui-icon",     //菜单图片,选填
 	    "menuSort":1,         //菜单顺序,必填
 	    "menuType":"0",		//菜单类型,0-系统菜单,1-标准业务菜单,2-IBMS菜单,3-APP菜单,必填
+	    "extraMenu":{"disable":true}	//菜单额外字段,json格式,选填
 	    "remark":"一级菜单备注"    //备注,选填
 	}
 
@@ -2911,10 +2913,12 @@ Header:Content-Type=application/json
 	    "menuCode":"1001",     //菜单编码,必填
 	    "menuName":"一级菜单",         //菜单名称,必填
 	    "menuUrl":"/query/list",    //菜单URL,选填
+	    "menuType":"0",   		 //分组菜单,0-非分组菜单,1-分组菜单,默认0,选填
 	    "urlType":"0",	//URL类型,0-相对路径,1-绝对路径,选填,当 menuUrl 不为空时,此处必填
 	    "menuIcon":"layui-icon",     //菜单图片,选填
 	    "menuSort":1,         //菜单顺序,必填
 	    "menuType":"0",		//菜单类型,0-系统菜单,1-标准业务菜单,2-IBMS菜单,3-APP菜单,必填
+	    "extraMenu":{"disable":true}	//菜单额外字段,json格式,选填
 	    "remark":"一级菜单备注"    //备注,选填
 	}