Browse Source

Merge remote-tracking branch 'origin/master'

caozj 2 năm trước cách đây
mục cha
commit
48124e99ce

+ 8 - 0
bladex/blade-auth/src/main/java/org/springblade/auth/constant/WxAppletConstant.java

@@ -0,0 +1,8 @@
+package org.springblade.auth.constant;
+
+public interface WxAppletConstant {
+
+    String STORAGE_APPID = "wxe4a17c2bc0287527";
+    String STORAGE_SECRET = "5129449cda3bb2232cde6f17f28549eb";
+
+}

+ 145 - 55
bladex/blade-auth/src/main/java/org/springblade/auth/granter/CaptchaTokenGranter.java

@@ -1,10 +1,15 @@
 package org.springblade.auth.granter;
 
+import org.springblade.auth.service.BladeUserDetails;
 import org.springblade.auth.utils.TokenUtil;
 import org.springblade.common.cache.CacheNames;
 import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.tool.utils.ObjectUtil;
+import org.springblade.core.tool.utils.SpringUtil;
 import org.springblade.core.tool.utils.StringUtil;
 import org.springblade.core.tool.utils.WebUtil;
+import org.springblade.system.user.entity.User;
+import org.springblade.system.user.feign.IUserClient;
 import org.springframework.security.authentication.*;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
@@ -24,59 +29,144 @@ import java.util.Map;
  */
 public class CaptchaTokenGranter extends AbstractTokenGranter {
 
-	private static final String GRANT_TYPE = "captcha";
-
-	private final AuthenticationManager authenticationManager;
-
-	private BladeRedis bladeRedis;
-
-	public CaptchaTokenGranter(AuthenticationManager authenticationManager,
-							   AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, BladeRedis bladeRedis) {
-		this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
-		this.bladeRedis = bladeRedis;
-	}
-
-	protected CaptchaTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,
-												ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
-		super(tokenServices, clientDetailsService, requestFactory, grantType);
-		this.authenticationManager = authenticationManager;
-	}
-
-	@Override
-	protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
-		HttpServletRequest request = WebUtil.getRequest();
-		// 增加验证码判断
-		String key = request.getHeader(TokenUtil.CAPTCHA_HEADER_KEY);
-		String code = request.getHeader(TokenUtil.CAPTCHA_HEADER_CODE);
-		// 获取验证码
-		String redisCode = bladeRedis.get(CacheNames.CAPTCHA_KEY + key);
-		// 判断验证码
-		if (code == null || !StringUtil.equalsIgnoreCase(redisCode, code)) {
-			throw new UserDeniedAuthorizationException(TokenUtil.CAPTCHA_NOT_CORRECT);
-		}
-
-		Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
-		String username = parameters.get("username");
-		String password = parameters.get("password");
-		// Protect from downstream leaks of password
-		parameters.remove("password");
-
-		Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
-		((AbstractAuthenticationToken) userAuth).setDetails(parameters);
-		try {
-			userAuth = authenticationManager.authenticate(userAuth);
-		}
-		catch (AccountStatusException | BadCredentialsException ase) {
-			//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
-			throw new InvalidGrantException(ase.getMessage());
-		}
-		// If the username/password are wrong the spec says we should send 400/invalid grant
-
-		if (userAuth == null || !userAuth.isAuthenticated()) {
-			throw new InvalidGrantException("Could not authenticate user: " + username);
-		}
-
-		OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
-		return new OAuth2Authentication(storedOAuth2Request, userAuth);
-	}
+    private static final String GRANT_TYPE = "captcha";
+
+    private final AuthenticationManager authenticationManager;
+
+    private BladeRedis bladeRedis;
+
+    public CaptchaTokenGranter(AuthenticationManager authenticationManager,
+                               AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, BladeRedis bladeRedis) {
+        this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
+        this.bladeRedis = bladeRedis;
+    }
+
+    protected CaptchaTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,
+                                  ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
+        super(tokenServices, clientDetailsService, requestFactory, grantType);
+        this.authenticationManager = authenticationManager;
+    }
+
+    @Override
+    protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
+
+        Map<String, String> parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters());
+
+        HttpServletRequest request = WebUtil.getRequest();
+
+        String type = parameters.get("type");
+
+        Authentication userAuth;
+
+        switch (type) {
+            // 小程序登录逻辑
+            case "storageApplet":
+                userAuth = storageAppletLogin(parameters, request);
+                break;
+            default:
+                // 校验验证码
+                verificationCode(request);
+                // 校验账号密码
+                userAuth = verificationAccountPassword(parameters, request);
+        }
+
+        OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
+        return new OAuth2Authentication(storedOAuth2Request, userAuth);
+    }
+
+    /**
+     * 校验验证码
+     */
+    private void verificationCode(HttpServletRequest request) {
+
+        // 增加验证码判断
+        String key = request.getHeader(TokenUtil.CAPTCHA_HEADER_KEY);
+        String code = request.getHeader(TokenUtil.CAPTCHA_HEADER_CODE);
+        if (ObjectUtil.isEmpty(key) || ObjectUtil.isEmpty(code)) {
+            throw new UserDeniedAuthorizationException(TokenUtil.CAPTCHA_NOT_CORRECT);
+        }
+        // 获取验证码
+        String redisCode = bladeRedis.get(CacheNames.CAPTCHA_KEY + key);
+        // 删除验证码缓存
+        bladeRedis.del(CacheNames.CAPTCHA_KEY + key);
+
+        // 判断验证码
+        if (!StringUtil.equalsIgnoreCase(redisCode, code)) {
+            throw new UserDeniedAuthorizationException(TokenUtil.CAPTCHA_NOT_CORRECT);
+        }
+    }
+
+    /**
+     * 校验账号密码
+     */
+    private Authentication verificationAccountPassword(Map<String, String> parameters, HttpServletRequest request) {
+
+        String username = parameters.get("username");
+        String password = parameters.get("password");
+        // Protect from downstream leaks of password
+        parameters.remove("password");
+
+        // 把密码放入attribute中
+        request.setAttribute(TokenUtil.PASSWORD_KEY, password);
+
+        Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
+        ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
+        try {
+            userAuth = authenticationManager.authenticate(userAuth);
+        } catch (AccountStatusException | BadCredentialsException ase) {
+            //covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
+            throw new InvalidGrantException(ase.getMessage());
+        }
+        // If the username/password are wrong the spec says we should send 400/invalid grant
+
+        if (userAuth == null || !userAuth.isAuthenticated()) {
+            throw new InvalidGrantException("Could not authenticate user: " + username);
+        }
+
+        return userAuth;
+    }
+
+    /**
+     * 杰生小程序登录
+     */
+    private Authentication storageAppletLogin(Map<String, String> parameters, HttpServletRequest request) {
+        Authentication userAuth;
+
+        String username = parameters.get("username");
+        String password = parameters.get("password");
+        String jsCode = parameters.get("jsCode");
+
+        // 获取openId
+        String openId = "odMjv5Yndizr4ybYaShMt9s9GMVI";
+//        String openId = WxAppletUtil.getOpenId(WxAppletConstant.STORAGE_APPID, WxAppletConstant.STORAGE_SECRET, jsCode);
+
+        IUserClient userClient = SpringUtil.getBean(IUserClient.class);
+
+        if (ObjectUtil.isNotEmpty(username) && ObjectUtil.isNotEmpty(password)) {
+            userAuth = verificationAccountPassword(parameters, request);
+
+            BladeUserDetails bladeUserDetails = (BladeUserDetails) userAuth.getPrincipal();
+            Long userId = bladeUserDetails.getUserId();
+
+            User user = new User();
+            user.setId(userId);
+            user.setAppletKey(password);
+            user.setAppletOpenId(openId);
+
+            userClient.updateUserById(user);
+        } else {
+            try {
+                User user = userClient.getUserInfoByAppletOpenId(openId);
+                parameters.put("username", user.getAccount());
+                parameters.put("password", user.getAppletKey());
+
+                userAuth = verificationAccountPassword(parameters, request);
+            } catch (Exception e) {
+                throw new UserDeniedAuthorizationException("无法自动登录");
+            }
+        }
+
+        return userAuth;
+    }
+
 }

+ 115 - 115
bladex/blade-auth/src/main/java/org/springblade/auth/service/BladeUserDetailsServiceImpl.java

@@ -50,121 +50,121 @@ import java.util.List;
 @AllArgsConstructor
 public class BladeUserDetailsServiceImpl implements UserDetailsService {
 
-	public static final Integer FAIL_COUNT = 5;
-
-	private final IUserClient userClient;
-	private final ISysClient sysClient;
-
-	private final BladeRedis bladeRedis;
-
-	@Override
-	@SneakyThrows
-	public BladeUserDetails loadUserByUsername(String username) {
-		HttpServletRequest request = WebUtil.getRequest();
-		// 获取用户绑定ID
-		String headerDept = request.getHeader(TokenUtil.DEPT_HEADER_KEY);
-		String headerRole = request.getHeader(TokenUtil.ROLE_HEADER_KEY);
-		// 获取租户ID
-		String headerTenant = request.getHeader(TokenUtil.TENANT_HEADER_KEY);
-		String paramTenant = request.getParameter(TokenUtil.TENANT_PARAM_KEY);
-		String password = request.getParameter(TokenUtil.PASSWORD_KEY);
-		String grantType = request.getParameter(TokenUtil.GRANT_TYPE_KEY);
-		if (StringUtil.isAllBlank(headerTenant, paramTenant)) {
-			throw new UserDeniedAuthorizationException(TokenUtil.TENANT_NOT_FOUND);
-		}
-		String tenantId = StringUtils.isBlank(headerTenant) ? paramTenant : headerTenant;
-
-		// 判断登录是否锁定
-		// TODO 2.8.3版本将增加:1.参数管理读取配置 2.用户管理增加解封按钮
-		int count = getFailCount(tenantId, username);
-		if (count >= FAIL_COUNT) {
-			throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_TOO_MANY_FAILS);
-		}
-
-		// 获取租户信息
-		R<Tenant> tenant = sysClient.getTenant(tenantId);
-		if (tenant.isSuccess()) {
-			if (TokenUtil.judgeTenant(tenant.getData())) {
-				throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION);
-			}
-		} else {
-			throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT);
-		}
-
-		// 获取用户类型
-		String userType = Func.toStr(request.getHeader(TokenUtil.USER_TYPE_HEADER_KEY), TokenUtil.DEFAULT_USER_TYPE);
-
-		// 远程调用返回数据
-		R<UserInfo> result;
-		// 根据不同用户类型调用对应的接口返回数据,用户可自行拓展
-		if (userType.equals(UserEnum.WEB.getName())) {
-			result = userClient.userInfo(tenantId, username, UserEnum.WEB.getName());
-		} else if (userType.equals(UserEnum.APP.getName())) {
-			result = userClient.userInfo(tenantId, username, UserEnum.APP.getName());
-		} else {
-			result = userClient.userInfo(tenantId, username, UserEnum.OTHER.getName());
-		}
-
-		// 判断返回信息
-		if (result.isSuccess()) {
-			UserInfo userInfo = result.getData();
-			User user = userInfo.getUser();
-			// 用户不存在,但提示用户名与密码错误并锁定账号
-			if (user == null || user.getId() == null) {
-				setFailCount(tenantId, username, count);
-				throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
-			}
-			// 用户存在但密码错误,超过次数则锁定账号
-			if (!grantType.equals(TokenUtil.REFRESH_TOKEN_KEY) && !user.getPassword().equals(DigestUtil.hex(password))) {
-				setFailCount(tenantId, username, count);
-				throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
-			}
-			// 用户角色不存在
-			if (Func.isEmpty(userInfo.getRoles())) {
-				throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_ROLE);
-			}
-			// 多部门情况下指定单部门
-			if (Func.isNotEmpty(headerDept) && user.getDeptId().contains(headerDept)) {
-				user.setDeptId(headerDept);
-			}
-			// 多角色情况下指定单角色
-			if (Func.isNotEmpty(headerRole) && user.getRoleId().contains(headerRole)) {
-				R<List<String>> roleResult = sysClient.getRoleAliases(headerRole);
-				if (roleResult.isSuccess()) {
-					userInfo.setRoles(roleResult.getData());
-				}
-				user.setRoleId(headerRole);
-			}
-			return new BladeUserDetails(user.getId(),
-				user.getTenantId(), StringPool.EMPTY, user.getName(), user.getRealName(), user.getDeptId(), user.getPostId(), user.getRoleId(), Func.join(userInfo.getRoles()), Func.toStr(user.getAvatar(), TokenUtil.DEFAULT_AVATAR),
-				username, AuthConstant.ENCRYPT + user.getPassword(), userInfo.getDetail(), true, true, true, true,
-				AuthorityUtils.commaSeparatedStringToAuthorityList(Func.join(result.getData().getRoles())));
-		} else {
-			throw new UsernameNotFoundException(result.getMsg());
-		}
-	}
-
-	/**
-	 * 获取账号错误次数
-	 *
-	 * @param tenantId 租户id
-	 * @param username 账号
-	 * @return int
-	 */
-	private int getFailCount(String tenantId, String username) {
-		return Func.toInt(bladeRedis.get(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username)), 0);
-	}
-
-	/**
-	 * 设置账号错误次数
-	 *
-	 * @param tenantId 租户id
-	 * @param username 账号
-	 * @param count    次数
-	 */
-	private void setFailCount(String tenantId, String username, int count) {
-		bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username), count + 1, Duration.ofMinutes(30));
-	}
+    public static final Integer FAIL_COUNT = 5;
+
+    private final IUserClient userClient;
+    private final ISysClient sysClient;
+
+    private final BladeRedis bladeRedis;
+
+    @Override
+    @SneakyThrows
+    public BladeUserDetails loadUserByUsername(String username) {
+        HttpServletRequest request = WebUtil.getRequest();
+        // 获取用户绑定ID
+        String headerDept = request.getHeader(TokenUtil.DEPT_HEADER_KEY);
+        String headerRole = request.getHeader(TokenUtil.ROLE_HEADER_KEY);
+        // 获取租户ID
+        String headerTenant = request.getHeader(TokenUtil.TENANT_HEADER_KEY);
+        String paramTenant = request.getParameter(TokenUtil.TENANT_PARAM_KEY);
+        String password = request.getAttribute(TokenUtil.PASSWORD_KEY).toString();
+        String grantType = request.getParameter(TokenUtil.GRANT_TYPE_KEY);
+        if (StringUtil.isAllBlank(headerTenant, paramTenant)) {
+            throw new UserDeniedAuthorizationException(TokenUtil.TENANT_NOT_FOUND);
+        }
+        String tenantId = StringUtils.isBlank(headerTenant) ? paramTenant : headerTenant;
+
+        // 判断登录是否锁定
+        // TODO 2.8.3版本将增加:1.参数管理读取配置 2.用户管理增加解封按钮
+        int count = getFailCount(tenantId, username);
+        if (count >= FAIL_COUNT) {
+            throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_TOO_MANY_FAILS);
+        }
+
+        // 获取租户信息
+        R<Tenant> tenant = sysClient.getTenant(tenantId);
+        if (tenant.isSuccess()) {
+            if (TokenUtil.judgeTenant(tenant.getData())) {
+                throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION);
+            }
+        } else {
+            throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT);
+        }
+
+        // 获取用户类型
+        String userType = Func.toStr(request.getHeader(TokenUtil.USER_TYPE_HEADER_KEY), TokenUtil.DEFAULT_USER_TYPE);
+
+        // 远程调用返回数据
+        R<UserInfo> result;
+        // 根据不同用户类型调用对应的接口返回数据,用户可自行拓展
+        if (userType.equals(UserEnum.WEB.getName())) {
+            result = userClient.userInfo(tenantId, username, UserEnum.WEB.getName());
+        } else if (userType.equals(UserEnum.APP.getName())) {
+            result = userClient.userInfo(tenantId, username, UserEnum.APP.getName());
+        } else {
+            result = userClient.userInfo(tenantId, username, UserEnum.OTHER.getName());
+        }
+
+        // 判断返回信息
+        if (result.isSuccess()) {
+            UserInfo userInfo = result.getData();
+            User user = userInfo.getUser();
+            // 用户不存在,但提示用户名与密码错误并锁定账号
+            if (user == null || user.getId() == null) {
+                setFailCount(tenantId, username, count);
+                throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
+            }
+            // 用户存在但密码错误,超过次数则锁定账号
+            if (!grantType.equals(TokenUtil.REFRESH_TOKEN_KEY) && !user.getPassword().equals(DigestUtil.hex(password))) {
+                setFailCount(tenantId, username, count);
+                throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
+            }
+            // 用户角色不存在
+            if (Func.isEmpty(userInfo.getRoles())) {
+                throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_ROLE);
+            }
+            // 多部门情况下指定单部门
+            if (Func.isNotEmpty(headerDept) && user.getDeptId().contains(headerDept)) {
+                user.setDeptId(headerDept);
+            }
+            // 多角色情况下指定单角色
+            if (Func.isNotEmpty(headerRole) && user.getRoleId().contains(headerRole)) {
+                R<List<String>> roleResult = sysClient.getRoleAliases(headerRole);
+                if (roleResult.isSuccess()) {
+                    userInfo.setRoles(roleResult.getData());
+                }
+                user.setRoleId(headerRole);
+            }
+            return new BladeUserDetails(user.getId(),
+                    user.getTenantId(), StringPool.EMPTY, user.getName(), user.getRealName(), user.getDeptId(), user.getPostId(), user.getRoleId(), Func.join(userInfo.getRoles()), Func.toStr(user.getAvatar(), TokenUtil.DEFAULT_AVATAR),
+                    username, AuthConstant.ENCRYPT + user.getPassword(), userInfo.getDetail(), true, true, true, true,
+                    AuthorityUtils.commaSeparatedStringToAuthorityList(Func.join(result.getData().getRoles())));
+        } else {
+            throw new UsernameNotFoundException(result.getMsg());
+        }
+    }
+
+    /**
+     * 获取账号错误次数
+     *
+     * @param tenantId 租户id
+     * @param username 账号
+     * @return int
+     */
+    private int getFailCount(String tenantId, String username) {
+        return Func.toInt(bladeRedis.get(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username)), 0);
+    }
+
+    /**
+     * 设置账号错误次数
+     *
+     * @param tenantId 租户id
+     * @param username 账号
+     * @param count    次数
+     */
+    private void setFailCount(String tenantId, String username, int count) {
+        bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username), count + 1, Duration.ofMinutes(30));
+    }
 
 
 }

+ 20 - 0
bladex/blade-auth/src/main/java/org/springblade/auth/utils/WxAppletUtil.java

@@ -0,0 +1,20 @@
+package org.springblade.auth.utils;
+
+import com.alibaba.fastjson.JSONObject;
+import org.springframework.web.client.RestTemplate;
+
+public class WxAppletUtil {
+
+    private static final RestTemplate restTemplate = new RestTemplate();
+
+    public static String getOpenId(String appid, String secret, String code) {
+
+        String wxResult = restTemplate.getForObject("https://api.weixin.qq.com/sns/jscode2session?appid="
+                + appid + "&secret=" + secret + "&js_code=" + code + "&grant_type=authorization_code", String.class);
+
+        JSONObject wxResultMap = JSONObject.parseObject(wxResult);
+
+        return wxResultMap.get("openid").toString();
+    }
+
+}

+ 60 - 57
bladex/blade-service-api/blade-user-api/src/main/java/org/springblade/system/user/entity/User.java

@@ -33,64 +33,67 @@ import java.util.Date;
 @EqualsAndHashCode(callSuper = true)
 public class User extends TenantEntity {
 
-	private static final long serialVersionUID = 1L;
+    private static final long serialVersionUID = 1L;
 
-	/**
-	 * 用户编号
-	 */
-	private String code;
-	/**
-	 * 用户平台
-	 */
-	private Integer userType;
-	/**
-	 * 账号
-	 */
-	private String account;
-	/**
-	 * 密码
-	 */
-	private String password;
-	/**
-	 * 昵称
-	 */
-	private String name;
-	/**
-	 * 真名
-	 */
-	private String realName;
-	/**
-	 * 头像
-	 */
-	private String avatar;
-	/**
-	 * 邮箱
-	 */
-	private String email;
-	/**
-	 * 手机
-	 */
-	private String phone;
-	/**
-	 * 生日
-	 */
-	private Date birthday;
-	/**
-	 * 性别
-	 */
-	private Integer sex;
-	/**
-	 * 角色id
-	 */
-	private String roleId;
-	/**
-	 * 部门id
-	 */
-	private String deptId;
-	/**
-	 * 岗位id
-	 */
-	private String postId;
+    /**
+     * 用户编号
+     */
+    private String code;
+    /**
+     * 用户平台
+     */
+    private Integer userType;
+    /**
+     * 账号
+     */
+    private String account;
+    /**
+     * 密码
+     */
+    private String password;
+    /**
+     * 昵称
+     */
+    private String name;
+    /**
+     * 真名
+     */
+    private String realName;
+    /**
+     * 头像
+     */
+    private String avatar;
+    /**
+     * 邮箱
+     */
+    private String email;
+    /**
+     * 手机
+     */
+    private String phone;
+    /**
+     * 生日
+     */
+    private Date birthday;
+    /**
+     * 性别
+     */
+    private Integer sex;
+    /**
+     * 角色id
+     */
+    private String roleId;
+    /**
+     * 部门id
+     */
+    private String deptId;
+    /**
+     * 岗位id
+     */
+    private String postId;
 
+    private String appletKey;
+
+    private String appletOpenId;
 
 }

+ 113 - 97
bladex/blade-service-api/blade-user-api/src/main/java/org/springblade/system/user/feign/IUserClient.java

@@ -36,104 +36,120 @@ import java.util.List;
  * @author Chill
  */
 @FeignClient(
-	value = AppConstant.APPLICATION_USER_NAME
+        value = AppConstant.APPLICATION_USER_NAME
 )
 public interface IUserClient {
 
-	String API_PREFIX = "/client";
-	String USER_INFO = API_PREFIX + "/user-info";
-	String USER_INFO_BY_TYPE = API_PREFIX + "/user-info-by-type";
-	String USER_INFO_BY_ID = API_PREFIX + "/user-info-by-id";
-	String USER_INFO_BY_ACCOUNT = API_PREFIX + "/user-info-by-account";
-	String USER_AUTH_INFO = API_PREFIX + "/user-auth-info";
-	String SAVE_USER = API_PREFIX + "/save-user";
-	String REMOVE_USER = API_PREFIX + "/remove-user";
-	String INFO_USER= API_PREFIX + "/info-user";
-	String USER_INFO_BY_IDS = API_PREFIX + "/user-info-by-ids";
-	/**
-	 * 获取用户信息
-	 *
-	 * @param userId 用户id
-	 * @return
-	 */
-	@GetMapping(USER_INFO_BY_ID)
-	R<User> userInfoById(@RequestParam("userId") Long userId);
-
-
-	/**
-	 * 根据账号获取用户信息
-	 *
-	 * @param tenantId 租户id
-	 * @param account  账号
-	 * @return
-	 */
-	@GetMapping(USER_INFO_BY_ACCOUNT)
-	R<User> userByAccount(@RequestParam("tenantId") String tenantId, @RequestParam("account") String account);
-
-	/**
-	 * 获取用户信息
-	 *
-	 * @param tenantId 租户ID
-	 * @param account  账号
-	 * @return
-	 */
-	@GetMapping(USER_INFO)
-	R<UserInfo> userInfo(@RequestParam("tenantId") String tenantId, @RequestParam("account") String account);
-
-	/**
-	 * 获取用户信息
-	 *
-	 * @param tenantId 租户ID
-	 * @param account  账号
-	 * @param userType 用户平台
-	 * @return
-	 */
-	@GetMapping(USER_INFO_BY_TYPE)
-	R<UserInfo> userInfo(@RequestParam("tenantId") String tenantId, @RequestParam("account") String account, @RequestParam("userType") String userType);
-
-	/**
-	 * 获取第三方平台信息
-	 *
-	 * @param userOauth 第三方授权用户信息
-	 * @return UserInfo
-	 */
-	@PostMapping(USER_AUTH_INFO)
-	R<UserInfo> userAuthInfo(@RequestBody UserOauth userOauth);
-
-	/**
-	 * 新建用户
-	 *
-	 * @param user 用户实体
-	 * @return
-	 */
-	@PostMapping(SAVE_USER)
-	R<Boolean> saveUser(@RequestBody User user);
-
-	/**
-	 * 删除用户
-	 *
-	 * @param tenantIds 租户id集合
-	 * @return
-	 */
-	@PostMapping(REMOVE_USER)
-	R<Boolean> removeUser(@RequestParam("tenantIds") String tenantIds);
-
-	/**
-	 * 删除用户
-	 *
-	 * @param id 主键
-	 * @return
-	 */
-	@PostMapping(INFO_USER)
-	User infoUser(@RequestParam("id") String id);
-
-
-	/**
-	 * 根据用户id查询用户信息
-	 *
-	 * @param ids 用户id集合
-	 * @return
-	 */
-	@PostMapping(USER_INFO_BY_IDS)
-	List<User> userInfoByIds(@RequestBody List<String> ids);
+    String API_PREFIX = "/client";
+    String USER_INFO = API_PREFIX + "/user-info";
+    String USER_INFO_BY_TYPE = API_PREFIX + "/user-info-by-type";
+    String USER_INFO_BY_ID = API_PREFIX + "/user-info-by-id";
+    String USER_INFO_BY_ACCOUNT = API_PREFIX + "/user-info-by-account";
+    String USER_AUTH_INFO = API_PREFIX + "/user-auth-info";
+    String SAVE_USER = API_PREFIX + "/save-user";
+    String REMOVE_USER = API_PREFIX + "/remove-user";
+    String INFO_USER = API_PREFIX + "/info-user";
+    String USER_INFO_BY_IDS = API_PREFIX + "/user-info-by-ids";
+    String UPDATE_USER_BY_ID = API_PREFIX + "/user-update-by-id";
+    String GET_USER_INFO_BY_APPLET_OPEN_ID = API_PREFIX + "/get-user-info-by-id";
+
+    /**
+     * 获取用户信息
+     *
+     * @param userId 用户id
+     * @return
+     */
+    @GetMapping(USER_INFO_BY_ID)
+    R<User> userInfoById(@RequestParam("userId") Long userId);
+
+
+    /**
+     * 根据账号获取用户信息
+     *
+     * @param tenantId 租户id
+     * @param account  账号
+     * @return
+     */
+    @GetMapping(USER_INFO_BY_ACCOUNT)
+    R<User> userByAccount(@RequestParam("tenantId") String tenantId, @RequestParam("account") String account);
+
+    /**
+     * 获取用户信息
+     *
+     * @param tenantId 租户ID
+     * @param account  账号
+     * @return
+     */
+    @GetMapping(USER_INFO)
+    R<UserInfo> userInfo(@RequestParam("tenantId") String tenantId, @RequestParam("account") String account);
+
+    /**
+     * 获取用户信息
+     *
+     * @param tenantId 租户ID
+     * @param account  账号
+     * @param userType 用户平台
+     * @return
+     */
+    @GetMapping(USER_INFO_BY_TYPE)
+    R<UserInfo> userInfo(@RequestParam("tenantId") String tenantId, @RequestParam("account") String account, @RequestParam("userType") String userType);
+
+    /**
+     * 获取第三方平台信息
+     *
+     * @param userOauth 第三方授权用户信息
+     * @return UserInfo
+     */
+    @PostMapping(USER_AUTH_INFO)
+    R<UserInfo> userAuthInfo(@RequestBody UserOauth userOauth);
+
+    /**
+     * 新建用户
+     *
+     * @param user 用户实体
+     * @return
+     */
+    @PostMapping(SAVE_USER)
+    R<Boolean> saveUser(@RequestBody User user);
+
+    /**
+     * 删除用户
+     *
+     * @param tenantIds 租户id集合
+     * @return
+     */
+    @PostMapping(REMOVE_USER)
+    R<Boolean> removeUser(@RequestParam("tenantIds") String tenantIds);
+
+    /**
+     * 删除用户
+     *
+     * @param id 主键
+     * @return
+     */
+    @PostMapping(INFO_USER)
+    User infoUser(@RequestParam("id") String id);
+
+
+    /**
+     * 根据用户id查询用户信息
+     *
+     * @param ids 用户id集合
+     * @return
+     */
+    @PostMapping(USER_INFO_BY_IDS)
+    List<User> userInfoByIds(@RequestBody List<String> ids);
+
+    /**
+     * 更根据用户id编辑用户数据
+     */
+    @PostMapping(UPDATE_USER_BY_ID)
+    void updateUserById(@RequestBody User user);
+
+    /**
+     * 根据openId获取用户信息
+     */
+    @PostMapping(GET_USER_INFO_BY_APPLET_OPEN_ID)
+    User getUserInfoByAppletOpenId(@RequestParam("openId") String openId);
+
 }

+ 67 - 53
bladex/blade-service/blade-user/src/main/java/org/springblade/system/user/feign/UserClient.java

@@ -43,58 +43,72 @@ import java.util.List;
 @AllArgsConstructor
 public class UserClient implements IUserClient {
 
-	private final IUserService service;
-
-	@Override
-	@GetMapping(USER_INFO_BY_ID)
-	public R<User> userInfoById(Long userId) {
-		return R.data(service.getById(userId));
-	}
-
-	@Override
-	@GetMapping(USER_INFO_BY_ACCOUNT)
-	public R<User> userByAccount(String tenantId, String account) {
-		return R.data(service.userByAccount(tenantId, account));
-	}
-
-	@Override
-	@GetMapping(USER_INFO)
-	public R<UserInfo> userInfo(String tenantId, String account) {
-		return R.data(service.userInfo(tenantId, account));
-	}
-
-	@Override
-	@GetMapping(USER_INFO_BY_TYPE)
-	public R<UserInfo> userInfo(String tenantId, String account, String userType) {
-		return R.data(service.userInfo(tenantId, account, UserEnum.of(userType)));
-	}
-
-	@Override
-	@PostMapping(USER_AUTH_INFO)
-	public R<UserInfo> userAuthInfo(@RequestBody UserOauth userOauth) {
-		return R.data(service.userInfo(userOauth));
-	}
-
-	@Override
-	@PostMapping(SAVE_USER)
-	public R<Boolean> saveUser(@RequestBody User user) {
-		return R.data(service.submit(user));
-	}
-
-	@Override
-	@PostMapping(REMOVE_USER)
-	public R<Boolean> removeUser(String tenantIds) {
-		return R.data(service.remove(Wrappers.<User>query().lambda().in(User::getTenantId, Func.toStrList(tenantIds))));
-	}
-	@PostMapping(INFO_USER)
-	@Override
-	public User infoUser(String id) {
-		return service.getById(id);
-	}
-	@PostMapping(USER_INFO_BY_IDS)
-	@Override
-	public List<User> userInfoByIds(List<String> ids) {
-		return service.list(Wrappers.<User>query().lambda().in(User::getId,ids));
-	}
+    private final IUserService service;
+
+    @Override
+    @GetMapping(USER_INFO_BY_ID)
+    public R<User> userInfoById(Long userId) {
+        return R.data(service.getById(userId));
+    }
+
+    @Override
+    @GetMapping(USER_INFO_BY_ACCOUNT)
+    public R<User> userByAccount(String tenantId, String account) {
+        return R.data(service.userByAccount(tenantId, account));
+    }
+
+    @Override
+    @GetMapping(USER_INFO)
+    public R<UserInfo> userInfo(String tenantId, String account) {
+        return R.data(service.userInfo(tenantId, account));
+    }
+
+    @Override
+    @GetMapping(USER_INFO_BY_TYPE)
+    public R<UserInfo> userInfo(String tenantId, String account, String userType) {
+        return R.data(service.userInfo(tenantId, account, UserEnum.of(userType)));
+    }
+
+    @Override
+    @PostMapping(USER_AUTH_INFO)
+    public R<UserInfo> userAuthInfo(@RequestBody UserOauth userOauth) {
+        return R.data(service.userInfo(userOauth));
+    }
+
+    @Override
+    @PostMapping(SAVE_USER)
+    public R<Boolean> saveUser(@RequestBody User user) {
+        return R.data(service.submit(user));
+    }
+
+    @Override
+    @PostMapping(REMOVE_USER)
+    public R<Boolean> removeUser(String tenantIds) {
+        return R.data(service.remove(Wrappers.<User>query().lambda().in(User::getTenantId, Func.toStrList(tenantIds))));
+    }
+
+    @PostMapping(INFO_USER)
+    @Override
+    public User infoUser(String id) {
+        return service.getById(id);
+    }
+
+    @PostMapping(USER_INFO_BY_IDS)
+    @Override
+    public List<User> userInfoByIds(List<String> ids) {
+        return service.list(Wrappers.<User>query().lambda().in(User::getId, ids));
+    }
+
+    @PostMapping(UPDATE_USER_BY_ID)
+    @Override
+    public void updateUserById(User user) {
+        service.updateUserById(user);
+    }
+
+    @PostMapping(GET_USER_INFO_BY_APPLET_OPEN_ID)
+    @Override
+    public User getUserInfoByAppletOpenId(String openId) {
+        return service.getUserInfoByAppletOpenId(openId);
+    }
 
 }

+ 5 - 0
bladex/blade-service/blade-user/src/main/java/org/springblade/system/user/service/IUserService.java

@@ -205,4 +205,9 @@ public interface IUserService extends BaseService<User> {
 	 * @return
 	 */
 	UserVO platformDetail(User user);
+
+    void updateUserById(User user);
+
+	User getUserInfoByAppletOpenId(String openId);
+
 }

+ 380 - 356
bladex/blade-service/blade-user/src/main/java/org/springblade/system/user/service/impl/UserServiceImpl.java

@@ -19,12 +19,14 @@ package org.springblade.system.user.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.AllArgsConstructor;
 import org.springblade.common.constant.CommonConstant;
 import org.springblade.common.constant.TenantConstant;
 import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.mp.base.BaseEntity;
 import org.springblade.core.mp.base.BaseServiceImpl;
 import org.springblade.core.mp.support.Condition;
 import org.springblade.core.mp.support.Query;
@@ -69,361 +71,383 @@ import static org.springblade.common.constant.CommonConstant.DEFAULT_PARAM_PASSW
 @Service
 @AllArgsConstructor
 public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implements IUserService {
-	private static final String GUEST_NAME = "guest";
-
-	private final IUserDeptService userDeptService;
-	private final IUserOauthService userOauthService;
-	private final ISysClient sysClient;
-	private final BladeTenantProperties tenantProperties;
-
-	@Override
-	@Transactional(rollbackFor = Exception.class)
-	public boolean submit(User user) {
-		if (StringUtil.isBlank(user.getTenantId())) {
-			user.setTenantId(BladeConstant.ADMIN_TENANT_ID);
-		}
-		String tenantId = user.getTenantId();
-		Tenant tenant = SysCache.getTenant(tenantId);
-		if (Func.isNotEmpty(tenant)) {
-			Integer accountNumber = tenant.getAccountNumber();
-			if (tenantProperties.getLicense()) {
-				String licenseKey = tenant.getLicenseKey();
-				String decrypt = DesUtil.decryptFormHex(licenseKey, TenantConstant.DES_KEY);
-				accountNumber = JsonUtil.parse(decrypt, Tenant.class).getAccountNumber();
-			}
-			Integer tenantCount = baseMapper.selectCount(Wrappers.<User>query().lambda().eq(User::getTenantId, tenantId));
-			if (accountNumber != null && accountNumber > 0 && accountNumber <= tenantCount) {
-				throw new ServiceException("当前租户已到最大账号额度!");
-			}
-		}
-		if (Func.isNotEmpty(user.getPassword())) {
-			user.setPassword(DigestUtil.encrypt(user.getPassword()));
-		}
-		Integer userCount = baseMapper.selectCount(Wrappers.<User>query().lambda().eq(User::getTenantId, tenantId).eq(User::getAccount, user.getAccount()));
-		if (userCount > 0 && Func.isEmpty(user.getId())) {
-			throw new ServiceException(StringUtil.format("当前用户 [{}] 已存在!", user.getAccount()));
-		}
-		return save(user) && submitUserDept(user);
-	}
-
-	@Override
-	@Transactional(rollbackFor = Exception.class)
-	public boolean updateUser(User user) {
-		String tenantId = user.getTenantId();
-		Integer userCount = baseMapper.selectCount(
-			Wrappers.<User>query().lambda()
-				.eq(User::getTenantId, tenantId)
-				.eq(User::getAccount, user.getAccount())
-				.notIn(User::getId, user.getId())
-		);
-		if (userCount > 0) {
-			throw new ServiceException(StringUtil.format("当前用户 [{}] 已存在!", user.getAccount()));
-		}
-		return updateUserInfo(user) && submitUserDept(user);
-	}
-
-	@Override
-	public boolean updateUserInfo(User user) {
-		user.setPassword(null);
-		return updateById(user);
-	}
-
-	private boolean submitUserDept(User user) {
-		List<Long> deptIdList = Func.toLongList(user.getDeptId());
-		List<UserDept> userDeptList = new ArrayList<>();
-		deptIdList.forEach(deptId -> {
-			UserDept userDept = new UserDept();
-			userDept.setUserId(user.getId());
-			userDept.setDeptId(deptId);
-			userDeptList.add(userDept);
-		});
-		userDeptService.remove(Wrappers.<UserDept>update().lambda().eq(UserDept::getUserId, user.getId()));
-		return userDeptService.saveBatch(userDeptList);
-	}
-
-	@Override
-	public IPage<User> selectUserPage(IPage<User> page, User user, Long deptId, String tenantId) {
-		List<Long> deptIdList = SysCache.getDeptChildIds(deptId);
-		return page.setRecords(baseMapper.selectUserPage(page, user, deptIdList, tenantId));
-	}
-
-	@Override
-	public IPage<UserVO> selectUserSearch(UserVO user, Query query) {
-		LambdaQueryWrapper<User> queryWrapper = Wrappers.<User>query().lambda();
-		if (StringUtil.isNotBlank(user.getName())) {
-			queryWrapper.like(User::getName, user.getName());
-		}
-		if (StringUtil.isNotBlank(user.getDeptName())) {
-			String deptIds = SysCache.getDeptIdsByFuzzy(AuthUtil.getTenantId(), user.getDeptName());
-			if (StringUtil.isNotBlank(deptIds)) {
-				queryWrapper.and(wrapper -> {
-					List<String> ids = Func.toStrList(deptIds);
-					ids.forEach(id -> wrapper.like(User::getDeptId, id).or());
-				});
-			}
-		}
-		if (StringUtil.isNotBlank(user.getPostName())) {
-			String postIds = SysCache.getPostIdsByFuzzy(AuthUtil.getTenantId(), user.getPostName());
-			if (StringUtil.isNotBlank(postIds)) {
-				queryWrapper.and(wrapper -> {
-					List<String> ids = Func.toStrList(postIds);
-					ids.forEach(id -> wrapper.like(User::getPostId, id).or());
-				});
-			}
-		}
-		IPage<User> pages = this.page(Condition.getPage(query), queryWrapper);
-		return UserWrapper.build().pageVO(pages);
-	}
-
-	@Override
-	public User userByAccount(String tenantId, String account) {
-		return baseMapper.selectOne(Wrappers.<User>query().lambda().eq(User::getTenantId, tenantId).eq(User::getAccount, account).eq(User::getIsDeleted, BladeConstant.DB_NOT_DELETED));
-	}
-
-	@Override
-	public UserInfo userInfo(Long userId) {
-		User user = baseMapper.selectById(userId);
-		return buildUserInfo(user);
-	}
-
-	@Override
-	public UserInfo userInfo(String tenantId, String account) {
-		User user = baseMapper.getUser(tenantId, account);
-		return buildUserInfo(user);
-	}
-
-	@Override
-	public UserInfo userInfo(String tenantId, String account, UserEnum userEnum) {
-		User user = baseMapper.getUser(tenantId, account);
-		return buildUserInfo(user, userEnum);
-	}
-
-	private UserInfo buildUserInfo(User user) {
-		return buildUserInfo(user, UserEnum.WEB);
-	}
-
-	private UserInfo buildUserInfo(User user, UserEnum userEnum) {
-		if (ObjectUtil.isEmpty(user)) {
-			return null;
-		}
-		UserInfo userInfo = new UserInfo();
-		userInfo.setUser(user);
-		if (Func.isNotEmpty(user)) {
-			R<List<String>> result = sysClient.getRoleAliases(user.getRoleId());
-			if (result.isSuccess()) {
-				List<String> roleAlias = result.getData();
-				userInfo.setRoles(roleAlias);
-			}
-		}
-		// 根据每个用户平台,建立对应的detail表,通过查询将结果集写入到detail字段
-		Kv detail = Kv.create().set("type", userEnum.getName());
-		if (userEnum == UserEnum.WEB) {
-			UserWeb userWeb = new UserWeb();
-			UserWeb query = userWeb.selectOne(Wrappers.<UserWeb>lambdaQuery().eq(UserWeb::getUserId, user.getId()));
-			if (ObjectUtil.isNotEmpty(query)) {
-				detail.set("ext", query.getUserExt());
-			}
-		} else if (userEnum == UserEnum.APP) {
-			UserApp userApp = new UserApp();
-			UserApp query = userApp.selectOne(Wrappers.<UserApp>lambdaQuery().eq(UserApp::getUserId, user.getId()));
-			if (ObjectUtil.isNotEmpty(query)) {
-				detail.set("ext", query.getUserExt());
-			}
-		} else {
-			UserOther userOther = new UserOther();
-			UserOther query = userOther.selectOne(Wrappers.<UserOther>lambdaQuery().eq(UserOther::getUserId, user.getId()));
-			if (ObjectUtil.isNotEmpty(query)) {
-				detail.set("ext", query.getUserExt());
-			}
-		}
-		userInfo.setDetail(detail);
-		return userInfo;
-	}
-
-	@Override
-	@Transactional(rollbackFor = Exception.class)
-	public UserInfo userInfo(UserOauth userOauth) {
-		UserOauth uo = userOauthService.getOne(Wrappers.<UserOauth>query().lambda().eq(UserOauth::getUuid, userOauth.getUuid()).eq(UserOauth::getSource, userOauth.getSource()));
-		UserInfo userInfo;
-		if (Func.isNotEmpty(uo) && Func.isNotEmpty(uo.getUserId())) {
-			userInfo = this.userInfo(uo.getUserId());
-			userInfo.setOauthId(Func.toStr(uo.getId()));
-		} else {
-			userInfo = new UserInfo();
-			if (Func.isEmpty(uo)) {
-				userOauthService.save(userOauth);
-				userInfo.setOauthId(Func.toStr(userOauth.getId()));
-			} else {
-				userInfo.setOauthId(Func.toStr(uo.getId()));
-			}
-			User user = new User();
-			user.setAccount(userOauth.getUsername());
-			user.setTenantId(userOauth.getTenantId());
-			userInfo.setUser(user);
-			userInfo.setRoles(Collections.singletonList(GUEST_NAME));
-		}
-		return userInfo;
-	}
-
-	@Override
-	public boolean grant(String userIds, String roleIds) {
-		User user = new User();
-		user.setRoleId(roleIds);
-		return this.update(user, Wrappers.<User>update().lambda().in(User::getId, Func.toLongList(userIds)));
-	}
-
-	@Override
-	public boolean resetPassword(String userIds) {
-		User user = new User();
-		user.setPassword(DigestUtil.encrypt(CommonConstant.DEFAULT_PASSWORD));
-		user.setUpdateTime(DateUtil.now());
-		return this.update(user, Wrappers.<User>update().lambda().in(User::getId, Func.toLongList(userIds)));
-	}
-
-	@Override
-	public boolean updatePassword(Long userId, String oldPassword, String newPassword, String newPassword1) {
-		User user = getById(userId);
-		if (!newPassword.equals(newPassword1)) {
-			throw new ServiceException("请输入正确的确认密码!");
-		}
-		if (!user.getPassword().equals(DigestUtil.hex(oldPassword))) {
-			throw new ServiceException("原密码不正确!");
-		}
-		return this.update(Wrappers.<User>update().lambda().set(User::getPassword, DigestUtil.hex(newPassword)).eq(User::getId, userId));
-	}
-
-	@Override
-	public boolean removeUser(String userIds) {
-		if (Func.contains(Func.toLongArray(userIds), AuthUtil.getUserId())) {
-			throw new ServiceException("不能删除本账号!");
-		}
-		return deleteLogic(Func.toLongList(userIds));
-	}
-
-	@Override
-	@Transactional(rollbackFor = Exception.class)
-	public void importUser(List<UserExcel> data, Boolean isCovered) {
-		data.forEach(userExcel -> {
-			User user = Objects.requireNonNull(BeanUtil.copy(userExcel, User.class));
-			// 设置用户平台
-			user.setUserType(Func.toInt(DictCache.getKey(DictEnum.USER_TYPE, userExcel.getUserTypeName()), 1));
-			// 设置部门ID
-			user.setDeptId(SysCache.getDeptIds(userExcel.getTenantId(), userExcel.getDeptName()));
-			// 设置岗位ID
-			user.setPostId(SysCache.getPostIds(userExcel.getTenantId(), userExcel.getPostName()));
-			// 设置角色ID
-			user.setRoleId(SysCache.getRoleIds(userExcel.getTenantId(), userExcel.getRoleName()));
-			// 设置租户ID
-			if (!AuthUtil.isAdministrator() || StringUtil.isBlank(user.getTenantId())) {
-				user.setTenantId(AuthUtil.getTenantId());
-			}
-			// 覆盖数据
-			if (isCovered) {
-				// 查询用户是否存在
-				User oldUser = UserCache.getUser(userExcel.getTenantId(), userExcel.getAccount());
-				if (oldUser != null && oldUser.getId() != null) {
-					user.setId(oldUser.getId());
-					this.updateUser(user);
-					return;
-				}
-			}
-			// 获取默认密码配置
-			String initPassword = ParamCache.getValue(DEFAULT_PARAM_PASSWORD);
-			user.setPassword(initPassword);
-			this.submit(user);
-		});
-	}
-
-	@Override
-	public List<UserExcel> exportUser(Wrapper<User> queryWrapper) {
-		List<UserExcel> userList = baseMapper.exportUser(queryWrapper);
-		userList.forEach(user -> {
-			user.setUserTypeName(DictCache.getValue(DictEnum.USER_TYPE, user.getUserType()));
-			user.setRoleName(StringUtil.join(SysCache.getRoleNames(user.getRoleId())));
-			user.setDeptName(StringUtil.join(SysCache.getDeptNames(user.getDeptId())));
-			user.setPostName(StringUtil.join(SysCache.getPostNames(user.getPostId())));
-		});
-		return userList;
-	}
-
-	@Override
-	@Transactional(rollbackFor = Exception.class)
-	public boolean registerGuest(User user, Long oauthId) {
-		Tenant tenant = SysCache.getTenant(user.getTenantId());
-		if (tenant == null || tenant.getId() == null) {
-			throw new ServiceException("租户信息错误!");
-		}
-		UserOauth userOauth = userOauthService.getById(oauthId);
-		if (userOauth == null || userOauth.getId() == null) {
-			throw new ServiceException("第三方登陆信息错误!");
-		}
-		user.setRealName(user.getName());
-		user.setAvatar(userOauth.getAvatar());
-		user.setRoleId(StringPool.MINUS_ONE);
-		user.setDeptId(StringPool.MINUS_ONE);
-		user.setPostId(StringPool.MINUS_ONE);
-		boolean userTemp = this.submit(user);
-		userOauth.setUserId(user.getId());
-		userOauth.setTenantId(user.getTenantId());
-		boolean oauthTemp = userOauthService.updateById(userOauth);
-		return (userTemp && oauthTemp);
-	}
-	@Override
-	public boolean updatePlatform(Long userId, Integer userType, String userExt) {
-		if (userType.equals(UserEnum.WEB.getCategory())) {
-			UserWeb userWeb = new UserWeb();
-			UserWeb query = userWeb.selectOne(Wrappers.<UserWeb>lambdaQuery().eq(UserWeb::getUserId, userId));
-			if (ObjectUtil.isNotEmpty(query)) {
-				userWeb.setId(query.getId());
-			}
-			userWeb.setUserId(userId);
-			userWeb.setUserExt(userExt);
-			return userWeb.insertOrUpdate();
-		} else if (userType.equals(UserEnum.APP.getCategory())) {
-			UserApp userApp = new UserApp();
-			UserApp query = userApp.selectOne(Wrappers.<UserApp>lambdaQuery().eq(UserApp::getUserId, userId));
-			if (ObjectUtil.isNotEmpty(query)) {
-				userApp.setId(query.getId());
-			}
-			userApp.setUserId(userId);
-			userApp.setUserExt(userExt);
-			return userApp.insertOrUpdate();
-		} else {
-			UserOther userOther = new UserOther();
-			UserOther query = userOther.selectOne(Wrappers.<UserOther>lambdaQuery().eq(UserOther::getUserId, userId));
-			if (ObjectUtil.isNotEmpty(query)) {
-				userOther.setId(query.getId());
-			}
-			userOther.setUserId(userId);
-			userOther.setUserExt(userExt);
-			return userOther.insertOrUpdate();
-		}
-	}
-
-	@Override
-	public UserVO platformDetail(User user) {
-		User detail = baseMapper.selectOne(Condition.getQueryWrapper(user));
-		UserVO userVO = UserWrapper.build().entityVO(detail);
-		if (userVO.getUserType().equals(UserEnum.WEB.getCategory())) {
-			UserWeb userWeb = new UserWeb();
-			UserWeb query = userWeb.selectOne(Wrappers.<UserWeb>lambdaQuery().eq(UserWeb::getUserId, user.getId()));
-			if (ObjectUtil.isNotEmpty(query)) {
-				userVO.setUserExt(query.getUserExt());
-			}
-		} else if (userVO.getUserType().equals(UserEnum.APP.getCategory())) {
-			UserApp userApp = new UserApp();
-			UserApp query = userApp.selectOne(Wrappers.<UserApp>lambdaQuery().eq(UserApp::getUserId, user.getId()));
-			if (ObjectUtil.isNotEmpty(query)) {
-				userVO.setUserExt(query.getUserExt());
-			}
-		} else {
-			UserOther userOther = new UserOther();
-			UserOther query = userOther.selectOne(Wrappers.<UserOther>lambdaQuery().eq(UserOther::getUserId, user.getId()));
-			if (ObjectUtil.isNotEmpty(query)) {
-				userVO.setUserExt(query.getUserExt());
-			}
-		}
-		return userVO;
-	}
+    private static final String GUEST_NAME = "guest";
+
+    private final IUserDeptService userDeptService;
+    private final IUserOauthService userOauthService;
+    private final ISysClient sysClient;
+    private final BladeTenantProperties tenantProperties;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean submit(User user) {
+        if (StringUtil.isBlank(user.getTenantId())) {
+            user.setTenantId(BladeConstant.ADMIN_TENANT_ID);
+        }
+        String tenantId = user.getTenantId();
+        Tenant tenant = SysCache.getTenant(tenantId);
+        if (Func.isNotEmpty(tenant)) {
+            Integer accountNumber = tenant.getAccountNumber();
+            if (tenantProperties.getLicense()) {
+                String licenseKey = tenant.getLicenseKey();
+                String decrypt = DesUtil.decryptFormHex(licenseKey, TenantConstant.DES_KEY);
+                accountNumber = JsonUtil.parse(decrypt, Tenant.class).getAccountNumber();
+            }
+            Integer tenantCount = baseMapper.selectCount(Wrappers.<User>query().lambda().eq(User::getTenantId, tenantId));
+            if (accountNumber != null && accountNumber > 0 && accountNumber <= tenantCount) {
+                throw new ServiceException("当前租户已到最大账号额度!");
+            }
+        }
+        if (Func.isNotEmpty(user.getPassword())) {
+            user.setPassword(DigestUtil.encrypt(user.getPassword()));
+        }
+        Integer userCount = baseMapper.selectCount(Wrappers.<User>query().lambda().eq(User::getTenantId, tenantId).eq(User::getAccount, user.getAccount()));
+        if (userCount > 0 && Func.isEmpty(user.getId())) {
+            throw new ServiceException(StringUtil.format("当前用户 [{}] 已存在!", user.getAccount()));
+        }
+        return save(user) && submitUserDept(user);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean updateUser(User user) {
+        String tenantId = user.getTenantId();
+        Integer userCount = baseMapper.selectCount(
+                Wrappers.<User>query().lambda()
+                        .eq(User::getTenantId, tenantId)
+                        .eq(User::getAccount, user.getAccount())
+                        .notIn(User::getId, user.getId())
+        );
+        if (userCount > 0) {
+            throw new ServiceException(StringUtil.format("当前用户 [{}] 已存在!", user.getAccount()));
+        }
+        return updateUserInfo(user) && submitUserDept(user);
+    }
+
+    @Override
+    public boolean updateUserInfo(User user) {
+        user.setPassword(null);
+        return updateById(user);
+    }
+
+    private boolean submitUserDept(User user) {
+        List<Long> deptIdList = Func.toLongList(user.getDeptId());
+        List<UserDept> userDeptList = new ArrayList<>();
+        deptIdList.forEach(deptId -> {
+            UserDept userDept = new UserDept();
+            userDept.setUserId(user.getId());
+            userDept.setDeptId(deptId);
+            userDeptList.add(userDept);
+        });
+        userDeptService.remove(Wrappers.<UserDept>update().lambda().eq(UserDept::getUserId, user.getId()));
+        return userDeptService.saveBatch(userDeptList);
+    }
+
+    @Override
+    public IPage<User> selectUserPage(IPage<User> page, User user, Long deptId, String tenantId) {
+        List<Long> deptIdList = SysCache.getDeptChildIds(deptId);
+        return page.setRecords(baseMapper.selectUserPage(page, user, deptIdList, tenantId));
+    }
+
+    @Override
+    public IPage<UserVO> selectUserSearch(UserVO user, Query query) {
+        LambdaQueryWrapper<User> queryWrapper = Wrappers.<User>query().lambda();
+        if (StringUtil.isNotBlank(user.getName())) {
+            queryWrapper.like(User::getName, user.getName());
+        }
+        if (StringUtil.isNotBlank(user.getDeptName())) {
+            String deptIds = SysCache.getDeptIdsByFuzzy(AuthUtil.getTenantId(), user.getDeptName());
+            if (StringUtil.isNotBlank(deptIds)) {
+                queryWrapper.and(wrapper -> {
+                    List<String> ids = Func.toStrList(deptIds);
+                    ids.forEach(id -> wrapper.like(User::getDeptId, id).or());
+                });
+            }
+        }
+        if (StringUtil.isNotBlank(user.getPostName())) {
+            String postIds = SysCache.getPostIdsByFuzzy(AuthUtil.getTenantId(), user.getPostName());
+            if (StringUtil.isNotBlank(postIds)) {
+                queryWrapper.and(wrapper -> {
+                    List<String> ids = Func.toStrList(postIds);
+                    ids.forEach(id -> wrapper.like(User::getPostId, id).or());
+                });
+            }
+        }
+        IPage<User> pages = this.page(Condition.getPage(query), queryWrapper);
+        return UserWrapper.build().pageVO(pages);
+    }
+
+    @Override
+    public User userByAccount(String tenantId, String account) {
+        return baseMapper.selectOne(Wrappers.<User>query().lambda().eq(User::getTenantId, tenantId).eq(User::getAccount, account).eq(User::getIsDeleted, BladeConstant.DB_NOT_DELETED));
+    }
+
+    @Override
+    public UserInfo userInfo(Long userId) {
+        User user = baseMapper.selectById(userId);
+        return buildUserInfo(user);
+    }
+
+    @Override
+    public UserInfo userInfo(String tenantId, String account) {
+        User user = baseMapper.getUser(tenantId, account);
+        return buildUserInfo(user);
+    }
+
+    @Override
+    public UserInfo userInfo(String tenantId, String account, UserEnum userEnum) {
+        User user = baseMapper.getUser(tenantId, account);
+        return buildUserInfo(user, userEnum);
+    }
+
+    private UserInfo buildUserInfo(User user) {
+        return buildUserInfo(user, UserEnum.WEB);
+    }
+
+    private UserInfo buildUserInfo(User user, UserEnum userEnum) {
+        if (ObjectUtil.isEmpty(user)) {
+            return null;
+        }
+        UserInfo userInfo = new UserInfo();
+        userInfo.setUser(user);
+        if (Func.isNotEmpty(user)) {
+            R<List<String>> result = sysClient.getRoleAliases(user.getRoleId());
+            if (result.isSuccess()) {
+                List<String> roleAlias = result.getData();
+                userInfo.setRoles(roleAlias);
+            }
+        }
+        // 根据每个用户平台,建立对应的detail表,通过查询将结果集写入到detail字段
+        Kv detail = Kv.create().set("type", userEnum.getName());
+        if (userEnum == UserEnum.WEB) {
+            UserWeb userWeb = new UserWeb();
+            UserWeb query = userWeb.selectOne(Wrappers.<UserWeb>lambdaQuery().eq(UserWeb::getUserId, user.getId()));
+            if (ObjectUtil.isNotEmpty(query)) {
+                detail.set("ext", query.getUserExt());
+            }
+        } else if (userEnum == UserEnum.APP) {
+            UserApp userApp = new UserApp();
+            UserApp query = userApp.selectOne(Wrappers.<UserApp>lambdaQuery().eq(UserApp::getUserId, user.getId()));
+            if (ObjectUtil.isNotEmpty(query)) {
+                detail.set("ext", query.getUserExt());
+            }
+        } else {
+            UserOther userOther = new UserOther();
+            UserOther query = userOther.selectOne(Wrappers.<UserOther>lambdaQuery().eq(UserOther::getUserId, user.getId()));
+            if (ObjectUtil.isNotEmpty(query)) {
+                detail.set("ext", query.getUserExt());
+            }
+        }
+        userInfo.setDetail(detail);
+        return userInfo;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public UserInfo userInfo(UserOauth userOauth) {
+        UserOauth uo = userOauthService.getOne(Wrappers.<UserOauth>query().lambda().eq(UserOauth::getUuid, userOauth.getUuid()).eq(UserOauth::getSource, userOauth.getSource()));
+        UserInfo userInfo;
+        if (Func.isNotEmpty(uo) && Func.isNotEmpty(uo.getUserId())) {
+            userInfo = this.userInfo(uo.getUserId());
+            userInfo.setOauthId(Func.toStr(uo.getId()));
+        } else {
+            userInfo = new UserInfo();
+            if (Func.isEmpty(uo)) {
+                userOauthService.save(userOauth);
+                userInfo.setOauthId(Func.toStr(userOauth.getId()));
+            } else {
+                userInfo.setOauthId(Func.toStr(uo.getId()));
+            }
+            User user = new User();
+            user.setAccount(userOauth.getUsername());
+            user.setTenantId(userOauth.getTenantId());
+            userInfo.setUser(user);
+            userInfo.setRoles(Collections.singletonList(GUEST_NAME));
+        }
+        return userInfo;
+    }
+
+    @Override
+    public boolean grant(String userIds, String roleIds) {
+        User user = new User();
+        user.setRoleId(roleIds);
+        return this.update(user, Wrappers.<User>update().lambda().in(User::getId, Func.toLongList(userIds)));
+    }
+
+    @Override
+    public boolean resetPassword(String userIds) {
+        User user = new User();
+        user.setPassword(DigestUtil.encrypt(CommonConstant.DEFAULT_PASSWORD));
+        user.setUpdateTime(DateUtil.now());
+        return this.update(user, Wrappers.<User>update().lambda().in(User::getId, Func.toLongList(userIds)));
+    }
+
+    @Override
+    public boolean updatePassword(Long userId, String oldPassword, String newPassword, String newPassword1) {
+        User user = getById(userId);
+        if (!newPassword.equals(newPassword1)) {
+            throw new ServiceException("请输入正确的确认密码!");
+        }
+        if (!user.getPassword().equals(DigestUtil.hex(oldPassword))) {
+            throw new ServiceException("原密码不正确!");
+        }
+        return this.update(Wrappers.<User>update().lambda().set(User::getPassword, DigestUtil.hex(newPassword)).eq(User::getId, userId));
+    }
+
+    @Override
+    public boolean removeUser(String userIds) {
+        if (Func.contains(Func.toLongArray(userIds), AuthUtil.getUserId())) {
+            throw new ServiceException("不能删除本账号!");
+        }
+        return deleteLogic(Func.toLongList(userIds));
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void importUser(List<UserExcel> data, Boolean isCovered) {
+        data.forEach(userExcel -> {
+            User user = Objects.requireNonNull(BeanUtil.copy(userExcel, User.class));
+            // 设置用户平台
+            user.setUserType(Func.toInt(DictCache.getKey(DictEnum.USER_TYPE, userExcel.getUserTypeName()), 1));
+            // 设置部门ID
+            user.setDeptId(SysCache.getDeptIds(userExcel.getTenantId(), userExcel.getDeptName()));
+            // 设置岗位ID
+            user.setPostId(SysCache.getPostIds(userExcel.getTenantId(), userExcel.getPostName()));
+            // 设置角色ID
+            user.setRoleId(SysCache.getRoleIds(userExcel.getTenantId(), userExcel.getRoleName()));
+            // 设置租户ID
+            if (!AuthUtil.isAdministrator() || StringUtil.isBlank(user.getTenantId())) {
+                user.setTenantId(AuthUtil.getTenantId());
+            }
+            // 覆盖数据
+            if (isCovered) {
+                // 查询用户是否存在
+                User oldUser = UserCache.getUser(userExcel.getTenantId(), userExcel.getAccount());
+                if (oldUser != null && oldUser.getId() != null) {
+                    user.setId(oldUser.getId());
+                    this.updateUser(user);
+                    return;
+                }
+            }
+            // 获取默认密码配置
+            String initPassword = ParamCache.getValue(DEFAULT_PARAM_PASSWORD);
+            user.setPassword(initPassword);
+            this.submit(user);
+        });
+    }
+
+    @Override
+    public List<UserExcel> exportUser(Wrapper<User> queryWrapper) {
+        List<UserExcel> userList = baseMapper.exportUser(queryWrapper);
+        userList.forEach(user -> {
+            user.setUserTypeName(DictCache.getValue(DictEnum.USER_TYPE, user.getUserType()));
+            user.setRoleName(StringUtil.join(SysCache.getRoleNames(user.getRoleId())));
+            user.setDeptName(StringUtil.join(SysCache.getDeptNames(user.getDeptId())));
+            user.setPostName(StringUtil.join(SysCache.getPostNames(user.getPostId())));
+        });
+        return userList;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean registerGuest(User user, Long oauthId) {
+        Tenant tenant = SysCache.getTenant(user.getTenantId());
+        if (tenant == null || tenant.getId() == null) {
+            throw new ServiceException("租户信息错误!");
+        }
+        UserOauth userOauth = userOauthService.getById(oauthId);
+        if (userOauth == null || userOauth.getId() == null) {
+            throw new ServiceException("第三方登陆信息错误!");
+        }
+        user.setRealName(user.getName());
+        user.setAvatar(userOauth.getAvatar());
+        user.setRoleId(StringPool.MINUS_ONE);
+        user.setDeptId(StringPool.MINUS_ONE);
+        user.setPostId(StringPool.MINUS_ONE);
+        boolean userTemp = this.submit(user);
+        userOauth.setUserId(user.getId());
+        userOauth.setTenantId(user.getTenantId());
+        boolean oauthTemp = userOauthService.updateById(userOauth);
+        return (userTemp && oauthTemp);
+    }
+
+    @Override
+    public boolean updatePlatform(Long userId, Integer userType, String userExt) {
+        if (userType.equals(UserEnum.WEB.getCategory())) {
+            UserWeb userWeb = new UserWeb();
+            UserWeb query = userWeb.selectOne(Wrappers.<UserWeb>lambdaQuery().eq(UserWeb::getUserId, userId));
+            if (ObjectUtil.isNotEmpty(query)) {
+                userWeb.setId(query.getId());
+            }
+            userWeb.setUserId(userId);
+            userWeb.setUserExt(userExt);
+            return userWeb.insertOrUpdate();
+        } else if (userType.equals(UserEnum.APP.getCategory())) {
+            UserApp userApp = new UserApp();
+            UserApp query = userApp.selectOne(Wrappers.<UserApp>lambdaQuery().eq(UserApp::getUserId, userId));
+            if (ObjectUtil.isNotEmpty(query)) {
+                userApp.setId(query.getId());
+            }
+            userApp.setUserId(userId);
+            userApp.setUserExt(userExt);
+            return userApp.insertOrUpdate();
+        } else {
+            UserOther userOther = new UserOther();
+            UserOther query = userOther.selectOne(Wrappers.<UserOther>lambdaQuery().eq(UserOther::getUserId, userId));
+            if (ObjectUtil.isNotEmpty(query)) {
+                userOther.setId(query.getId());
+            }
+            userOther.setUserId(userId);
+            userOther.setUserExt(userExt);
+            return userOther.insertOrUpdate();
+        }
+    }
+
+    @Override
+    public UserVO platformDetail(User user) {
+        User detail = baseMapper.selectOne(Condition.getQueryWrapper(user));
+        UserVO userVO = UserWrapper.build().entityVO(detail);
+        if (userVO.getUserType().equals(UserEnum.WEB.getCategory())) {
+            UserWeb userWeb = new UserWeb();
+            UserWeb query = userWeb.selectOne(Wrappers.<UserWeb>lambdaQuery().eq(UserWeb::getUserId, user.getId()));
+            if (ObjectUtil.isNotEmpty(query)) {
+                userVO.setUserExt(query.getUserExt());
+            }
+        } else if (userVO.getUserType().equals(UserEnum.APP.getCategory())) {
+            UserApp userApp = new UserApp();
+            UserApp query = userApp.selectOne(Wrappers.<UserApp>lambdaQuery().eq(UserApp::getUserId, user.getId()));
+            if (ObjectUtil.isNotEmpty(query)) {
+                userVO.setUserExt(query.getUserExt());
+            }
+        } else {
+            UserOther userOther = new UserOther();
+            UserOther query = userOther.selectOne(Wrappers.<UserOther>lambdaQuery().eq(UserOther::getUserId, user.getId()));
+            if (ObjectUtil.isNotEmpty(query)) {
+                userVO.setUserExt(query.getUserExt());
+            }
+        }
+        return userVO;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateUserById(User user) {
+
+        updateById(user);
+
+        if (ObjectUtil.isNotEmpty(user.getAppletOpenId())) {
+            LambdaUpdateWrapper<User> updateWrapper = Wrappers.lambdaUpdate();
+            updateWrapper.set(User::getAppletKey, null)
+                    .set(User::getAppletOpenId, null)
+                    .ne(BaseEntity::getId, user.getId());
+        }
+
+    }
+
+    @Override
+    public User getUserInfoByAppletOpenId(String openId) {
+        LambdaQueryWrapper<User> wrapper = Wrappers.<User>lambdaQuery().eq(User::getAppletOpenId, openId);
+        return getOne(wrapper);
+    }
 
 }

+ 16 - 0
hx-service/storage/src/main/java/com/fjhx/applet/LoginConfig.java

@@ -0,0 +1,16 @@
+package com.fjhx.applet;
+
+import org.springblade.core.tool.api.R;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/stockAttachment")
+public class LoginConfig {
+
+    public R login() {
+
+        return R.success();
+    }
+
+}

+ 15 - 13
hx-service/storage/src/main/java/com/fjhx/stock/mapper/StockWaterMapper.xml

@@ -80,19 +80,21 @@
     </select>
 
     <select id="remainingTodayPage" resultType="java.util.LinkedHashMap">
-        SELECT m.ID                                                                               materialId,
-               mc.Name                                                                            categoryName,
-               m.Name                                                                             materialName,
-               m.Code                                                                             materialCode,
-               m.Width                                                                            materialWidth,
-               sum(sd.Quantity)                                                                   sum,
-               count(sd.Quantity)                                                                 count,
-               sum(IF(datediff(now(), sd.CreatedTime) > m.DelayPeriod, sd.Quantity, 0))           retentionQuantity,
-               m.SafetyStock                                                                      materialSafetyStock,
-               IF((a.CheckTagCount = a.TagQuantity) and (a.CheckTagCount = a.HandTagCount), 1, 0) inventoryResults,
-               a.CheckTagCount - a.TagQuantity                                                    correctionQuantity,
-               a.CheckTime                                                                        checkTime,
-               uu.RealName                                                                        OperUserName
+        SELECT m.ID                            materialId,
+               mc.Name                         categoryName,
+               m.Name                          materialName,
+               m.Code                          materialCode,
+               m.Width                         materialWidth,
+               sum(sd.Quantity)                sum,
+               count(sd.Quantity)              count,
+               sum(IF(datediff(now(), sd.CreatedTime) > m.DelayPeriod, sd.Quantity,
+                      0))                      retentionQuantity,
+               m.SafetyStock                   materialSafetyStock,
+               IF((a.CheckTagCount = a.TagQuantity) and (a.CheckTagCount = a.HandTagCount), 1,
+                  0)                           inventoryResults,
+               a.CheckTagCount - a.TagQuantity correctionQuantity,
+               a.CheckTime                     checkTime,
+               uu.RealName                     OperUserName
         FROM material m
                  INNER JOIN stock_detail sd ON sd.MaterialCode = m.Code
                  LEFT JOIN (

+ 5 - 1
hx-service/storage/src/main/java/com/fjhx/stock/service/impl/StockWaterServiceImpl.java

@@ -399,7 +399,9 @@ public class StockWaterServiceImpl extends ServiceImpl<StockWaterMapper, StockWa
                         this::remainingTodayMerge
                 ));
 
-        result.put("purposeStatistics", new ArrayList<>(purposeStatistics.values()));
+        result.put("purposeStatistics", purposeStatistics.values().stream()
+                .sorted((v1, v2) -> ((BigDecimal) v2.get("measureArea")).compareTo((BigDecimal) v1.get("measureArea")))
+                .collect(Collectors.toList()));
 
         return result;
     }
@@ -445,6 +447,8 @@ public class StockWaterServiceImpl extends ServiceImpl<StockWaterMapper, StockWa
             }
         }
 
+        wrapper.orderByAsc("m.Name");
+
         // 查询基本信息
         Page<Map<String, Object>> page = baseMapper.remainingTodayPage(createPage(condition), wrapper);