Browse Source

新增扫码登录

tjf 3 years ago
parent
commit
eb7b626d89

+ 37 - 4
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java

@@ -2,11 +2,12 @@ package com.ruoyi.web.controller.system;
 
 import java.util.List;
 import java.util.Set;
+
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.framework.web.service.TokenService;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 import com.ruoyi.common.constant.Constants;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.domain.entity.SysMenu;
@@ -34,6 +35,9 @@ public class SysLoginController
     @Autowired
     private SysPermissionService permissionService;
 
+    @Autowired
+    private TokenService tokenService;
+
     /**
      * 登录方法
      * 
@@ -71,6 +75,7 @@ public class SysLoginController
         return ajax;
     }
 
+
     /**
      * 获取路由信息
      * 
@@ -83,4 +88,32 @@ public class SysLoginController
         List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
         return AjaxResult.success(menuService.buildMenus(menus));
     }
+
+    /**
+     * 产生uuid二维码
+     */
+    @PostMapping("getQrCode")
+    public R<?> getQrCode() {
+        String qrCode = tokenService.getQrCode();
+        return R.ok(qrCode);
+    }
+
+    /**
+     * 判断二维码是否被扫描
+     */
+    @GetMapping("getPool/{uuid}")
+    public R<?> getPool(@PathVariable String uuid) {
+        return tokenService.getPool(uuid);
+    }
+
+    /**
+     * 扫码成功进入确认登录页面记录状态
+     *
+     * @param uuid
+     * @return
+     */
+    @GetMapping(value = "scanCode/{uuid}")
+    public R<?> scanCode(@PathVariable String uuid) {
+        return tokenService.scanCode(uuid);
+    }
 }

+ 27 - 0
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java

@@ -1,8 +1,13 @@
 package com.ruoyi.web.controller.system;
 
 import java.util.List;
+import java.util.Set;
 import java.util.stream.Collectors;
 import javax.servlet.http.HttpServletResponse;
+
+import com.alibaba.fastjson.JSON;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.utils.ObjectUtils;
 import org.apache.commons.lang3.ArrayUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -234,4 +239,26 @@ public class SysUserController extends BaseController
         userService.insertUserAuth(userId, roleIds);
         return success();
     }
+
+
+    /**
+     * 获取当前用户信息
+     */
+    @PostMapping("/packInfo")
+    public LoginUser packInfo(@RequestBody SysUser sysUser) {
+        return userService.packPermDeptRoles(sysUser);
+    }
+
+
+    /**
+     * 功能描述: getByPhone
+     *
+     * @param phone phone
+     * @return com.boman.domain.SysUser
+     */
+    @GetMapping("/getByPhone/{phone}")
+    public SysUser getByPhone(@PathVariable("phone") String phone) {
+        SysUser sysUser = userService.getByPhone(phone);
+        return ObjectUtils.requireNonNull(sysUser, String.format("手机号 [%s] 对应的用户不存在", phone));
+    }
 }

+ 30 - 0
ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java

@@ -159,6 +159,36 @@ public class Constants
      */
     public static final String[] JOB_WHITELIST_STR = { "com.ruoyi" };
 
+    /**
+     * 令牌有效期(分钟)
+     */
+    public final static long TOKEN_EXPIRE = 120;
+
+    /**
+     * 授权成功
+     */
+    public static final Integer EMPOWER = 201;
+
+    /**
+     * 成功标记
+     */
+    public static final Integer SUCCESS_QR = 200;
+
+    /**
+     * 失败标记
+     */
+    public static final Integer FAIL_QR = 500;
+
+    /**
+     * 登录状态失效
+     */
+    public static final Integer LOGIN_EXPIRE = 401;
+
+    /**
+     * 扫码登录成功
+     */
+    public static final String LOGIN_SUCCESS_APP = "login_success:";
+
     /**
      * 定时任务违规的字符
      */

+ 116 - 0
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/R.java

@@ -0,0 +1,116 @@
+package com.ruoyi.common.core.domain;
+
+
+import com.ruoyi.common.constant.Constants;
+
+import java.io.Serializable;
+
+/**
+ * 响应信息主体
+ *
+ * @author ruoyi
+ */
+public class R<T> implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 成功 */
+    public static final int SUCCESS = Constants.SUCCESS_QR;
+
+    /** 失败 */
+    public static final int FAIL = Constants.FAIL_QR;
+    /**
+     * 登录状态失效
+     */
+    public static final Integer LOGIN_EXPIRE = Constants.LOGIN_EXPIRE;
+
+    private int code;
+
+    private String msg;
+
+    private T data;
+
+    public static <T> R<T> ok()
+    {
+        return restResult(null, SUCCESS, null);
+    }
+
+    public static <T> R<T> ok(T data)
+    {
+        return restResult(data, SUCCESS, null);
+    }
+
+    public static <T> R<T> ok(T data, String msg)
+    {
+        return restResult(data, SUCCESS, msg);
+    }
+
+    public static <T> R<T> fail()
+    {
+        return restResult(null, FAIL, null);
+    }
+
+    public static <T> R<T> fail(String msg)
+    {
+        return restResult(null, FAIL, msg);
+    }
+
+    public static <T> R<T> loginExpire(String msg)
+    {
+        return restResult(null, LOGIN_EXPIRE, msg);
+    }
+
+    public static <T> R<T> fail(T data)
+    {
+        return restResult(data, FAIL, null);
+    }
+
+    public static <T> R<T> fail(T data, String msg)
+    {
+        return restResult(data, FAIL, msg);
+    }
+
+    public static <T> R<T> fail(int code, String msg)
+    {
+        return restResult(null, code, msg);
+    }
+
+    private static <T> R<T> restResult(T data, int code, String msg)
+    {
+        R<T> apiResult = new R<>();
+        apiResult.setCode(code);
+        apiResult.setData(data);
+        apiResult.setMsg(msg);
+        return apiResult;
+    }
+
+    public int getCode()
+    {
+        return code;
+    }
+
+    public void setCode(int code)
+    {
+        this.code = code;
+    }
+
+    public String getMsg()
+    {
+        return msg;
+    }
+
+    public void setMsg(String msg)
+    {
+        this.msg = msg;
+    }
+
+    public T getData()
+    {
+        return data;
+    }
+
+    public void setData(T data)
+    {
+        this.data = data;
+    }
+}

+ 20 - 0
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/UserEnvConstant.java

@@ -0,0 +1,20 @@
+package com.ruoyi.common.core.domain;
+
+
+
+/**
+ * @author shiqian
+ * @date 2021年04月14日 09:46
+ **/
+public class UserEnvConstant {
+
+
+    public static String USER_ID = "user.id";
+    public static final String USERNAME = "user.name";
+    public static final String USER_DEPT_ID = "user.dept.id";
+    public static final String USER_DEPT_NAME = "user.dept.name";
+    public static final String USER_PARENT_DEPT_ID = "user.parent.dept.id";
+    public static final String USER_PARENT_DEPT_NAME = "user.parent.dept.name";
+
+
+}

+ 73 - 0
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java

@@ -1,7 +1,12 @@
 package com.ruoyi.common.core.domain.model;
 
 import java.util.Collection;
+import java.util.List;
 import java.util.Set;
+
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.entity.SysRole;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.userdetails.UserDetails;
 import com.alibaba.fastjson.annotation.JSONField;
@@ -71,6 +76,74 @@ public class LoginUser implements UserDetails
      */
     private SysUser user;
 
+
+    /**
+     * 用户变量 登录的时候塞进去的
+     */
+    private JSONObject userEnv;
+    /**
+     * 小程序登录的状态码
+     */
+    private Integer code;
+    /**
+     * 登录类型
+     */
+    private String loginType;
+
+    /**
+     * 用户账号
+     */
+    @Excel(name = "登录名称")
+    private String userName;
+
+    /**
+     * 角色列表
+     */
+    private Set<String> roles;
+
+    public Set<String> getRoles() {
+        return roles;
+    }
+
+    public void setRoles(Set<String> roles) {
+        this.roles = roles;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public void setCode(Integer code) {
+        this.code = code;
+    }
+
+    public String getLoginType() {
+        return loginType;
+    }
+
+    public void setLoginType(String loginType) {
+        this.loginType = loginType;
+    }
+
+    public JSONObject getUserEnv() {
+        return userEnv;
+    }
+
+    public void setUserEnv(JSONObject userEnv) {
+        this.userEnv = userEnv;
+    }
+
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+
+
     public Long getUserId()
     {
         return userId;

+ 264 - 0
ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisService.java

@@ -0,0 +1,264 @@
+package com.ruoyi.common.core.redis;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.BoundSetOperations;
+import org.springframework.data.redis.core.HashOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.ValueOperations;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * spring redis 工具类
+ * 
+ * @author ruoyi
+ **/
+@SuppressWarnings(value = { "unchecked", "rawtypes" })
+@Component
+public class RedisService
+{
+    @Autowired
+    public RedisTemplate redisTemplate;
+
+    /**
+     * 缓存基本的对象,Integer、String、实体类等
+     *
+     * @param key 缓存的键值
+     * @param value 缓存的值
+     */
+    public <T> void setCacheObject(final String key, final T value)
+    {
+        redisTemplate.opsForValue().set(key, value);
+    }
+
+    /**
+     * 功能描述: ttl
+     *
+     * @param key key
+     */
+    public <T> Long ttl(final String key) {
+        return redisTemplate.opsForValue().getOperations().getExpire(key);
+    }
+
+    /**
+     * 缓存基本的对象,Integer、String、实体类等
+     *
+     * @param key 缓存的键值
+     * @param value 缓存的值
+     * @param timeout 时间
+     * @param timeUnit 时间颗粒度
+     */
+    public <T> void setCacheObject(final String key, final T value, final Long timeout, final TimeUnit timeUnit)
+    {
+        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
+    }
+
+    /**
+     * 设置有效时间
+     *
+     * @param key Redis键
+     * @param timeout 超时时间
+     * @return true=设置成功;false=设置失败
+     */
+    public boolean expire(final String key, final long timeout)
+    {
+        return expire(key, timeout, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 设置有效时间
+     *
+     * @param key Redis键
+     * @param timeout 超时时间
+     * @param unit 时间单位
+     * @return true=设置成功;false=设置失败
+     */
+    public boolean expire(final String key, final long timeout, final TimeUnit unit)
+    {
+        return redisTemplate.expire(key, timeout, unit);
+    }
+
+    /**
+     * 获得缓存的基本对象。
+     *
+     * @param key 缓存键值
+     * @return 缓存键值对应的数据
+     */
+    public <T> T getCacheObject(final String key)
+    {
+        ValueOperations<String, T> operation = redisTemplate.opsForValue();
+        return operation.get(key);
+    }
+
+    /**
+     * 删除单个对象
+     *
+     * @param key
+     */
+    public boolean deleteObject(final String key)
+    {
+        return redisTemplate.delete(key);
+    }
+
+    /**
+     * 删除集合对象
+     *
+     * @param collection 多个对象
+     * @return
+     */
+    public long deleteObject(final Collection collection)
+    {
+        return redisTemplate.delete(collection);
+    }
+
+    /**
+     * 缓存List数据
+     *
+     * @param key 缓存的键值
+     * @param dataList 待缓存的List数据
+     * @return 缓存的对象
+     */
+    public <T> long setCacheList(final String key, final List<T> dataList)
+    {
+        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
+        return count == null ? 0 : count;
+    }
+
+    /**
+     * 获得缓存的list对象
+     *
+     * @param key 缓存的键值
+     * @return 缓存键值对应的数据
+     */
+    public <T> List<T> getCacheList(final String key)
+    {
+        return redisTemplate.opsForList().range(key, 0, -1);
+    }
+
+    /**
+     * 缓存Set
+     *
+     * @param key 缓存键值
+     * @param dataSet 缓存的数据
+     * @return 缓存数据的对象
+     */
+    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
+    {
+        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
+        Iterator<T> it = dataSet.iterator();
+        while (it.hasNext())
+        {
+            setOperation.add(it.next());
+        }
+        return setOperation;
+    }
+
+    /**
+     * 获得缓存的set
+     *
+     * @param key
+     * @return
+     */
+    public <T> Set<T> getCacheSet(final String key)
+    {
+        return redisTemplate.opsForSet().members(key);
+    }
+
+    /**
+     * 缓存Map
+     *
+     * @param key
+     * @param dataMap
+     */
+    public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
+    {
+        if (dataMap != null) {
+            redisTemplate.opsForHash().putAll(key, dataMap);
+        }
+    }
+
+    /**
+     * 获得缓存的Map
+     *
+     * @param key
+     * @return
+     */
+    public <T> Map<String, T> getCacheMap(final String key)
+    {
+        return redisTemplate.opsForHash().entries(key);
+    }
+
+    /**
+     * 往Hash中存入数据
+     *
+     * @param key Redis键
+     * @param hKey Hash键
+     * @param value 值
+     */
+    public <T> void setCacheMapValue(final String key, final String hKey, final T value)
+    {
+        redisTemplate.opsForHash().put(key, hKey, value);
+    }
+
+    /**
+     * 获取Hash中的数据
+     *
+     * @param key Redis键
+     * @param hKey Hash键
+     * @return Hash中的对象
+     */
+    public <T> T getCacheMapValue(final String key, final String hKey)
+    {
+        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
+        return opsForHash.get(key, hKey);
+    }
+
+    /**
+     * 获取多个Hash中的数据
+     *
+     * @param key Redis键
+     * @param hKeys Hash键集合
+     * @return Hash对象集合
+     */
+    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
+    {
+        return redisTemplate.opsForHash().multiGet(key, hKeys);
+    }
+
+    /**
+     * 获得缓存的基本对象列表
+     * 
+     * @param pattern 字符串前缀
+     * @return 对象列表
+     */
+    public Collection<String> keys(final String pattern)
+    {
+        return redisTemplate.keys(pattern);
+    }
+
+    /**
+     * 功能描述: 是否存在key
+     *
+     * @param key key
+     * @return 存在返回true, 不存在返回false
+     */
+    public boolean exists(String key) {
+        return redisTemplate.countExistingKeys(Collections.singleton(key)) >= 1;
+    }
+
+    /**
+     * 功能描述: increment
+     *
+     * @param key       key
+     * @param increment 增量
+     * @return long 增加以后的值
+     */
+
+    public int increment(String key, long... increment) {
+         return Integer.parseInt(redisTemplate.opsForValue().increment(key, ArrayUtils.isEmpty(increment) ? 1 : increment[0]).toString());
+    }
+
+}

+ 302 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/ObjectUtils.java

@@ -0,0 +1,302 @@
+package com.ruoyi.common.utils;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.ToIntFunction;
+import java.util.stream.Collectors;
+
+/**
+ * @author shiqian
+ * @date 2021年03月26日 10:39
+ **/
+public class ObjectUtils {
+
+    private static final String NULL = "null";
+    private static final String UNDEFINED = "undefined";
+
+    public static  <T> Collection<T> requireNonNull(Collection<T> input, String errorMsg){
+        if (null == input || input.size() == 0) {
+            throw new IllegalArgumentException(errorMsg);
+        }
+        return input;
+    }
+
+    public static  <T> List<T> requireNonNull(List<T> input, String errorMsg){
+        if (null == input || input.size() == 0) {
+            throw new IllegalArgumentException(errorMsg);
+        }
+        return input;
+    }
+
+    public static  <T> boolean isEmpty(Collection<T> input){
+        return null == input || input.size() == 0;
+    }
+
+    public static  <T> boolean isNotEmpty(Collection<T> input){
+        return !isEmpty(input);
+    }
+
+    public static long[] requireNonNull(long[] input, String errorMsg){
+        if (ArrayUtils.isEmpty(input)) {
+            throw new IllegalArgumentException(errorMsg);
+        }
+
+        return input;
+    }
+
+
+    public static Long[] requireNonNull(Long[] input, String errorMsg){
+        if (ArrayUtils.isEmpty(input)) {
+            throw new IllegalArgumentException(errorMsg);
+        }
+
+        return input;
+    }
+
+    public static Boolean requireNonNull(Boolean input, String errorMsg) {
+        if (input == null) {
+            throw new IllegalArgumentException(errorMsg);
+        }
+        return input;
+    }
+
+    public static <T> T requireNonNull(T input, String errorMsg) {
+        if (input == null) {
+            throw new IllegalArgumentException(errorMsg);
+        }
+        return input;
+    }
+
+    public static Integer requireNonNull(Integer input, String errorMsg) {
+        if (input == null || input < 0) {
+            throw new IllegalArgumentException(errorMsg);
+        }
+        return input;
+    }
+
+    public static boolean isEmpty(Integer input) {
+        return input == null || input < 0;
+    }
+
+    public static boolean isNotEmpty(Integer input) {
+        return !isEmpty(input);
+    }
+
+
+    public static boolean isEmpty(Long input) {
+        return input == null || input < 0;
+    }
+
+    public static boolean isNotEmpty(Long input) {
+        return !isEmpty(input);
+    }
+
+    public static String requireNonNull(String input, String errorMsg) {
+        if (input == null || input.isEmpty() || NULL.equalsIgnoreCase(input) || UNDEFINED.equalsIgnoreCase(input)) {
+            throw new IllegalArgumentException(errorMsg);
+        }
+        return input;
+    }
+
+    public static int requireNonNull(int input, String errorMsg) {
+        if (input <= 0) {
+            throw new IllegalArgumentException(errorMsg);
+        }
+        return input;
+    }
+
+
+    public static JSONObject requireNonNull(JSONObject input, String errorMsg) {
+        if (input == null || input.isEmpty()) {
+            throw new IllegalArgumentException(errorMsg);
+        }
+        return input;
+    }
+
+
+    public static JSONObject ifNullSetEmpty(JSONObject input) {
+        if (input == null || input.isEmpty()) {
+            input = new JSONObject();
+        }
+        return input;
+    }
+
+
+    public static List<JSONObject> ifNullSetEmpty(List<JSONObject> input) {
+        if (input == null || input.isEmpty()) {
+            input = Collections.emptyList();
+        }
+        return input;
+    }
+
+    public static JSONArray ifNullSetEmpty(JSONArray input) {
+        if (input == null || input.isEmpty()) {
+            input = new JSONArray();
+        }
+        return input;
+    }
+
+
+    public static boolean isNotEmpty(JSONObject input){
+        return input != null && !input.isEmpty();
+    }
+
+    public static boolean isEmpty(JSONObject input){
+        return !isNotEmpty(input);
+    }
+
+    public static boolean isNotEmpty(JSONArray input){
+        return input != null && !input.isEmpty();
+    }
+
+    public static boolean isEmpty(JSONArray input){
+        return !isNotEmpty(input);
+    }
+
+    /**
+     * 功能描述: 暂且只做 string collection long 三种类型的校验
+     *
+     * @param input input
+     * @return boolean
+     */
+    public static boolean isEmpty(Object input) {
+        if (input instanceof String) {
+            return StringUtils.isEmpty((String) input);
+        } else if (input instanceof Collection) {
+            return CollectionUtils.isEmpty(((Collection<?>) input));
+        } else {
+            return Objects.isNull(input);
+        }
+    }
+
+    public static boolean isNotEmpty(Object input) {
+        return !isEmpty(input);
+    }
+
+    public static boolean isEmpty(String input) {
+        return input == null || input.isEmpty() || NULL.equalsIgnoreCase(input) || UNDEFINED.equalsIgnoreCase(input);
+    }
+
+    public static boolean isNotEmpty(String input) {
+        return !isEmpty(input);
+    }
+
+    public static JSONArray requireNonNull(JSONArray input, String errorMsg) {
+        if (input == null || input.isEmpty()) {
+            throw new IllegalArgumentException(errorMsg);
+        }
+        return input;
+    }
+
+    /**
+     * 功能描述: 需要判断数据库是什么类型,如果是VARCHAR则需要转,如果是数字则无需转
+     *
+     * @param input 输入
+     * @return java.lang.String
+     */
+    public static String escapeStr(String input) {
+        return "'" + input + "'";
+    }
+
+
+    /**
+     * 功能描述: 是否小于0
+     *
+     * @param aLong aLong
+     * @return boolean true小于0
+     */
+    public static boolean ltZero(Long aLong) {
+        return null != aLong && aLong < 0;
+    }
+
+
+    /**
+     * 功能描述: 根据规则过滤
+     *
+     * @param input     原数据
+     * @param predicate FunctionalInterface
+     * @return java.util.List<T>
+     */
+    public static <T> List<T> filter(List<T> input, Predicate<T> predicate) {
+        return requireNonNull(input, "list is null")
+                .stream().filter(predicate).collect(Collectors.toList());
+    }
+
+    public static <T> int count(List<T> input, ToIntFunction<T> toIntFunction) {
+        return requireNonNull(input, "list is null").stream().mapToInt(toIntFunction).sum();
+    }
+
+    public static <T> boolean anyMatch(List<T> input, Predicate<T> predicate){
+        return requireNonNull(input, "list is null").stream().anyMatch(predicate);
+    }
+
+
+    public static <T> boolean noneMatch(List<T> input, Predicate<T> predicate) {
+        return requireNonNull(input, "list is null").stream().noneMatch(predicate);
+    }
+
+    /**
+     * 功能描述: 根据规则过滤
+     *
+     * @param input     原数据
+     * @param predicate FunctionalInterface
+     * @return java.util.List<T>
+     */
+    public static <T> T filterOne(List<T> input, Predicate<T> predicate) {
+        return requireNonNull(input, "list is null")
+                .stream().filter(predicate).findFirst().orElse(null);
+    }
+
+    /**
+     * 功能描述: 根据规则获取
+     *
+     * @param input    原数据
+     * @param function FunctionalInterface
+     * @return java.util.List<T>
+     */
+    public static <T, R> List<R> map(List<T> input, Function<T, R> function) {
+        return requireNonNull(input, "list is null")
+                .stream().map(function).distinct().collect(Collectors.toList());
+    }
+
+    /**
+     * 功能描述: 根据规则获取
+     *
+     * @param input     原数据
+     * @param predicate predicate
+     * @param function  FunctionalInterface
+     * @return java.util.List<R>
+     */
+    public static <T, R> List<R> mapFilter(List<T> input, Predicate<T> predicate, Function<T, R> function) {
+        return requireNonNull(input, "list is null")
+                .stream()
+                .filter(predicate)
+                .map(function)
+                .distinct()
+                .collect(Collectors.toList());
+    }
+
+
+    /**
+     * 功能描述: 根据规则获取单个
+     *
+     * @param input    原数据
+     * @param function FunctionalInterface
+     * @return java.util.List<T>
+     */
+    public static <T, R> R mapFirst(List<T> input, Function<T, R> function) {
+        requireNonNull(input, "list is null");
+        List<R> rList = input.stream().map(function).collect(Collectors.toList());
+        requireNonNull(rList, "rList is null");
+        Optional<R> optionalR = rList.stream().findFirst();
+        return optionalR.orElse(null);
+    }
+
+}

+ 166 - 0
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java

@@ -2,8 +2,23 @@ package com.ruoyi.framework.web.service;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.TimeUnit;
+import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
+
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.common.constant.CacheConstants;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.core.domain.UserEnvConstant;
+import com.ruoyi.common.core.domain.entity.SysDept;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.redis.RedisService;
+import com.ruoyi.system.service.ISysDeptService;
+import com.ruoyi.system.service.ISysUserService;
+import com.ruoyi.system.service.impl.SysUserServiceImpl;
+import org.apache.commons.lang3.ObjectUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
@@ -20,6 +35,8 @@ import io.jsonwebtoken.Claims;
 import io.jsonwebtoken.Jwts;
 import io.jsonwebtoken.SignatureAlgorithm;
 
+import static com.ruoyi.common.constant.Constants.*;
+
 /**
  * token验证处理
  *
@@ -46,9 +63,33 @@ public class TokenService
 
     private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;
 
+
+    private final static long EXPIRE_TIME_QR = 3 * 60;
+
+
+    private final static long EXPIRE_TIME = Constants.TOKEN_EXPIRE * 60;
+
+
+    private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY;
+
     @Autowired
     private RedisCache redisCache;
 
+
+    @Autowired
+    private RedisService redisService;
+
+    @Resource
+    private ISysUserService sysUserService;
+
+    @Autowired
+    private ISysDeptService deptService;
+
+    @Autowired
+    private TokenService tokenService;
+
+    @Autowired
+    private SysPermissionService permissionService;
     /**
      * 获取用户身份信息
      *
@@ -222,4 +263,129 @@ public class TokenService
     {
         return Constants.LOGIN_TOKEN_KEY + uuid;
     }
+
+
+
+    /**
+     * 生成二维码
+     *
+     * @return
+     */
+    public String getQrCode() {
+        String uuid = UUID.randomUUID().toString();
+        redisService.setCacheObject(uuid, EMPOWER, EXPIRE_TIME_QR, TimeUnit.SECONDS);
+        return uuid;
+    }
+
+
+    /**
+     * 判断二维码是否被扫描
+     *
+     * @param uuid
+     * @return
+     */
+    public R<?> getPool(String uuid) {
+        R<?> r = new R<>();
+        r.setMsg("un_scan");
+        if (redisService.getCacheObject(uuid) == null){
+            return  r;
+        }
+        String cacheObject = redisService.getCacheObject(uuid).toString();
+        try {
+            if (StringUtils.isBlank(cacheObject)) {
+                r.setMsg("该二维码已经失效,请重新获取");
+            } else if (cacheObject.equals(SUCCESS.toString())) {
+                r.setMsg("has_scan");
+            } else if (cacheObject.length() > 3) {
+                SysUser user = sysUserService.getByPhone(cacheObject.toString());
+                LoginUser loginUser = packPermDeptRoles(user);
+                //获取token
+                Map<String, Object> token = tokenService.createTokenQr(loginUser);
+                Object accessToken = token.get("access_token");
+                r.setCode(SUCCESS_QR);
+                r.setMsg(LOGIN_SUCCESS_APP+accessToken);
+                redisCache.deleteObject(uuid);
+                return r;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return r;
+    }
+
+    private LoginUser packPermDeptRoles(SysUser sysUser) {
+        LoginUser sysUserVo = new LoginUser();
+        // 角色集合
+        Set<String> roles = permissionService.getRolePermission(sysUser);
+        // 权限集合
+        Set<String> permissions = permissionService.getMenuPermission(sysUser);
+        sysUserVo.setUser(sysUser);
+        sysUserVo.setRoles(roles);
+        sysUserVo.setPermissions(permissions);
+        return sysUserVo;
+    }
+
+
+    /**
+     * 创建令牌
+     */
+    public Map<String, Object> createTokenQr(LoginUser loginUser) {
+        // 生成token
+        String token = IdUtils.fastUUID();
+        loginUser.setToken(token);
+        loginUser.setUserId(loginUser.getUser().getUserId());
+        loginUser.setUserName(loginUser.getUser().getUserName());
+        loginUser.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest()));
+
+        JSONObject userEnv = packUserEnv(loginUser);
+        loginUser.setUserEnv(userEnv);
+        loginUser.setLoginType("PC");
+        refreshToken(loginUser);
+
+        // 保存或更新用户token
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put("access_token", token);
+        map.put("expires_in", EXPIRE_TIME);
+        redisService.setCacheObject(ACCESS_TOKEN + token, loginUser, EXPIRE_TIME, TimeUnit.SECONDS);
+        return map;
+    }
+
+    private JSONObject packUserEnv(LoginUser loginUser) {
+        JSONObject userEnv = new JSONObject();
+        userEnv.put(UserEnvConstant.USER_ID, loginUser.getUserId());
+        userEnv.put(UserEnvConstant.USERNAME, loginUser.getUsername());
+
+        SysDept dept = loginUser.getUser().getDept();
+        userEnv.put(UserEnvConstant.USER_DEPT_ID, loginUser.getUser().getDeptId());
+        userEnv.put(UserEnvConstant.USER_DEPT_NAME, dept.getDeptName());
+
+        Long parentId = dept.getParentId();
+        SysDept parentDept = deptService.selectDeptById(parentId);
+        if (ObjectUtils.isNotEmpty(parentDept)) {
+            userEnv.put(UserEnvConstant.USER_PARENT_DEPT_ID, parentDept.getDeptId());
+            userEnv.put(UserEnvConstant.USER_PARENT_DEPT_NAME, parentDept.getDeptName());
+        }
+        return userEnv;
+    }
+
+    /**
+     * 用户扫码成功,改变redis中的值
+     *
+     * @param uuid
+     * @return
+     */
+    public R<String> scanCode(String uuid) {
+        R<String> r = new R<>();
+        Integer code = redisService.getCacheObject(uuid);
+        r.setCode(SUCCESS_QR);
+        r.setData(uuid);
+        if (code == null) {
+            r.setCode(FAIL_QR);
+            r.setMsg("该二维码已经失效,请重新获取");
+        }
+        if (code != null) {
+            redisService.setCacheObject(uuid, SUCCESS_QR);
+        }
+        return r;
+    }
 }

+ 7 - 0
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java

@@ -124,4 +124,11 @@ public interface SysUserMapper
      * @return 结果
      */
     public SysUser checkEmailUnique(String email);
+
+    /**
+     * 根据手机号获取用户信息
+     * @param phone
+     * @return
+     */
+    SysUser getByPhone(String phone);
 }

+ 10 - 0
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java

@@ -2,6 +2,7 @@ package com.ruoyi.system.service;
 
 import java.util.List;
 import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.domain.model.LoginUser;
 
 /**
  * 用户 业务层
@@ -203,4 +204,13 @@ public interface ISysUserService
      * @return 结果
      */
     public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName);
+
+
+    /**
+     * 功能描述: getByPhone
+     *
+     * @param phone phone
+     * @return com.boman.domain.SysUser
+     */
+    SysUser getByPhone(String phone);
 }

+ 14 - 0
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java

@@ -2,8 +2,12 @@ package com.ruoyi.system.service.impl;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 import java.util.stream.Collectors;
 import javax.validation.Validator;
+
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.system.service.ISysMenuService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -559,4 +563,14 @@ public class SysUserServiceImpl implements ISysUserService
         }
         return successMsg.toString();
     }
+
+    /**
+     * 根据手机号获取用户信息
+     * @param phone phone
+     * @return
+     */
+    @Override
+    public SysUser getByPhone(String phone) {
+        return userMapper.getByPhone(phone);
+    }
 }

+ 5 - 1
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml

@@ -217,5 +217,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  			#{userId}
         </foreach> 
  	</delete>
-	
+
+	<select id="getByPhone" resultMap="SysUserResult">
+		<include refid="selectUserVo"/>
+		where u.phonenumber = #{phone}  and u.del_flag = '0' and u.status = '0' limit 1;
+	</select>
 </mapper>