xucaiqin 2 сар өмнө
parent
commit
a777248eaf

+ 42 - 1
sckw-auth/src/main/java/com/sckw/auth/controller/AuthController.java

@@ -1,5 +1,8 @@
 package com.sckw.auth.controller;
 
+import cn.hutool.core.net.Ipv4Util;
+import cn.hutool.extra.servlet.JakartaServletUtil;
+import cn.hutool.extra.servlet.ServletUtil;
 import com.sckw.auth.model.vo.req.*;
 import com.sckw.auth.service.IAuthService;
 import com.sckw.core.model.enums.LoginMethodEnum;
@@ -49,6 +52,44 @@ public class AuthController {
         return authService.login(loginBase);
     }
 
+    @PostMapping("/quickLogin")
+    public HttpResult quickLogin(@Valid @RequestBody QuickLoginReqVo reqVo, HttpServletRequest request) {
+        int systemType = request.getIntHeader("System-Type");
+        String clientType = request.getHeader("Client-Type");
+        String accessSpecial = request.getHeader("Access-Special");
+        QuickLoginBase loginBase = new QuickLoginBase();
+        loginBase.setLoginMethod(LoginMethodEnum.QUICK.getValue());
+        loginBase.setSystemType(systemType);
+        loginBase.setClientType(clientType);
+        loginBase.setAccessSpecial(accessSpecial);
+        loginBase.setOpenid(reqVo.getOpenid());
+        loginBase.setDeviceId(reqVo.getDeviceId());
+        loginBase.setAccessToken(reqVo.getAccessToken());
+        loginBase.setIp(JakartaServletUtil.getClientIP(request));
+        return authService.quickLogin(loginBase);
+    }
+    @GetMapping("/getQr")
+    public HttpResult getQr(@RequestParam String token) {
+        return authService.getQr(token);
+    }
+
+    @GetMapping("/checkQr")
+    public HttpResult checkQr(@RequestParam String code) {
+        return authService.checkQr(code);
+    }
+    @GetMapping("/getQrCheckResult")
+    public HttpResult getQrCheckResult(@RequestParam String token) {
+        return authService.getQrCheckResult(token);
+    }
+    @GetMapping("/getSms")
+    public HttpResult getSms(@RequestParam String token) {
+        return authService.getSms(token);
+    }
+    @GetMapping("/checkSms")
+    public HttpResult checkSms(@RequestParam String token,@RequestParam String code) {
+        return authService.checkSms(token,code);
+    }
+
     /**
      * @return HttpResult
      * @desc: 根据token获取登录信息
@@ -74,4 +115,4 @@ public class AuthController {
         return HttpResult.ok(HttpStatus.MSG_001);
     }
 
-}
+}

+ 25 - 0
sckw-auth/src/main/java/com/sckw/auth/model/vo/req/CloudFunDto.java

@@ -0,0 +1,25 @@
+package com.sckw.auth.model.vo.req;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * {"code":0,"errCode":0,"errMsg":"","success":true,"phoneNumber":"15902849627"}
+ * @author xucaiqin
+ * @date 2025-10-23 10:00:21
+ */
+@NoArgsConstructor
+@Data
+public class CloudFunDto {
+    @JSONField(name = "code")
+    private Integer code;
+    @JSONField(name = "errCode")
+    private Integer errCode;
+    @JSONField(name = "errMsg")
+    private String errMsg;
+    @JSONField(name = "success")
+    private Boolean success;
+    @JSONField(name = "phoneNumber")
+    private String phoneNumber;
+}

+ 1 - 0
sckw-auth/src/main/java/com/sckw/auth/model/vo/req/LoginBase.java

@@ -19,6 +19,7 @@ public class LoginBase {
      * 密码
      */
     private String password;
+    private String deviceId;
 
     /**
      * 验证码

+ 34 - 0
sckw-auth/src/main/java/com/sckw/auth/model/vo/req/QuickLoginBase.java

@@ -0,0 +1,34 @@
+package com.sckw.auth.model.vo.req;
+
+import lombok.Data;
+
+
+@Data
+public class QuickLoginBase {
+
+    private String accessToken;
+    private String openid;
+    private String deviceId;
+
+    /**
+     * 系统类型(1 运营管理中心/2 运营管理中心/3 司机应用/4 官网)
+     */
+    private int systemType;
+
+    /**
+     * 客户端类型(ios 苹果设备/android 安卓设备/pc 浏览器/pc-background 管理系统)
+     */
+    private String clientType;
+
+    /**
+     * 专场标识
+     */
+    private String accessSpecial;
+
+    /**
+     * 登录方式1 账号密码登录/2账号短信登录/3单账号登录
+     */
+    private int loginMethod;
+    private String ip;
+
+}

+ 14 - 0
sckw-auth/src/main/java/com/sckw/auth/model/vo/req/QuickLoginReqVo.java

@@ -0,0 +1,14 @@
+package com.sckw.auth.model.vo.req;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+@Data
+public class QuickLoginReqVo {
+    @NotBlank(message = "accessToken不能为空")
+    private String accessToken;
+    @NotBlank(message = "openid不能为空")
+    private String openid;
+    @NotBlank(message = "deviceId不能为空")
+    private String deviceId;
+}

+ 21 - 0
sckw-auth/src/main/java/com/sckw/auth/model/vo/res/InfoDto.java

@@ -0,0 +1,21 @@
+package com.sckw.auth.model.vo.res;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @author xucaiqin
+ * @date 2025-10-26 11:24:04
+ */
+@Getter
+@Setter
+public class InfoDto implements Serializable {
+    @Serial
+    private static final long serialVersionUID = 8924389916820526372L;
+    private String phone;
+    private String deviceId;
+    private String city;//登录IP所在城市
+}

+ 45 - 0
sckw-auth/src/main/java/com/sckw/auth/service/IAuthService.java

@@ -51,4 +51,49 @@ public interface IAuthService {
      * @date: 2023/9/27
      */
     LoginResVo1 getLoginResByToken(String clientType, String token);
+
+    /**
+     * 一键登录
+     *
+     * @param loginBase
+     * @return
+     */
+    HttpResult quickLogin(QuickLoginBase loginBase);
+
+    /**
+     * 获取二维码 不需要登录
+     *
+     * @param token 登录后返回的临时token
+     * @return
+     */
+    HttpResult getQr(String token);
+
+    /**
+     * 旧设备扫码,校验二维码  需要登录
+     *
+     * @param code 二维码编码
+     * @return
+     */
+    HttpResult checkQr(String code);
+
+    /**
+     * 获取二维码校验结果,前端轮询调用
+     * @param token
+     * @return
+     */
+    HttpResult getQrCheckResult(String token);
+
+    /**
+     * 发送短信验证码
+     * @param token
+     * @return
+     */
+    HttpResult getSms(String token);
+
+    /**
+     * 校验短信验证码
+     * @param code
+     * @return
+     */
+    HttpResult checkSms(String token,String code);
 }

+ 331 - 12
sckw-auth/src/main/java/com/sckw/auth/service/impl/AuthServiceImpl.java

@@ -1,14 +1,22 @@
 package com.sckw.auth.service.impl;
 
+import cn.hutool.core.codec.Base64Encoder;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.qrcode.QrCodeUtil;
+import cn.hutool.extra.qrcode.QrConfig;
 import com.alibaba.fastjson.JSON;
-import com.sckw.auth.model.vo.req.ForgetPasswordReqVo;
-import com.sckw.auth.model.vo.req.LoginBase;
-import com.sckw.auth.model.vo.req.RegisterReqVo;
+import com.alibaba.fastjson.JSONObject;
+import com.sckw.auth.model.vo.req.*;
+import com.sckw.auth.model.vo.res.InfoDto;
 import com.sckw.auth.model.vo.res.LoginResVo;
 import com.sckw.auth.model.vo.res.LoginResVo1;
 import com.sckw.auth.service.IAuthService;
 import com.sckw.auth.util.AsyncFactory;
+import com.sckw.core.common.enums.EnvConstant;
 import com.sckw.core.common.enums.enums.DictEnum;
+import com.sckw.core.common.enums.enums.DictTypeEnum;
+import com.sckw.core.exception.BusinessException;
 import com.sckw.core.exception.SystemException;
 import com.sckw.core.model.constant.Global;
 import com.sckw.core.model.constant.NumberConstant;
@@ -24,19 +32,34 @@ import com.sckw.fleet.api.RemoteFleetService;
 import com.sckw.fleet.api.model.vo.RDriverDetailVo;
 import com.sckw.redis.constant.RedisConstant;
 import com.sckw.redis.utils.RedissonUtils;
+import com.sckw.stream.enums.SmsCodeEnum;
+import com.sckw.stream.model.SckwSms;
 import com.sckw.system.api.RemoteSystemService;
 import com.sckw.system.api.RemoteUserService;
 import com.sckw.system.api.model.dto.req.ForgetPasswordReqDto;
 import com.sckw.system.api.model.dto.req.RegisterReqDto;
 import com.sckw.system.api.model.dto.req.UserLoginReqDto;
 import com.sckw.system.api.model.dto.res.*;
+import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboReference;
 import org.redisson.api.RSet;
+import org.springframework.cloud.stream.function.StreamBridge;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 /**
@@ -50,6 +73,10 @@ public class AuthServiceImpl implements IAuthService {
 
     @DubboReference(version = "1.0.0", group = "design", check = false)
     private RemoteUserService remoteUserService;
+    @Resource
+    private RedisTemplate<String, Object> redisTemplate;
+    @Resource
+    private StreamBridge streamBridge;
 
     @DubboReference(version = "1.0.0", group = "design", check = false)
     private RemoteSystemService systemService;
@@ -70,8 +97,7 @@ public class AuthServiceImpl implements IAuthService {
         }
 
         /*运营端/企业端登录(PC/APP)*/
-        if (loginBase.getSystemType() == SystemTypeEnum.MANAGE.getCode()
-                || loginBase.getSystemType() == SystemTypeEnum.COMPANY.getCode()) {
+        if (loginBase.getSystemType() == SystemTypeEnum.MANAGE.getCode() || loginBase.getSystemType() == SystemTypeEnum.COMPANY.getCode()) {
             return this.commonAuth(loginBase);
         }
 
@@ -297,7 +323,7 @@ public class AuthServiceImpl implements IAuthService {
     }
 
     /**
-     * @param loginBase 登录参数
+     * @param loginBase  登录参数
      * @param enterprise 企业信息
      * @return 返回校验结果
      * @desc 专场标识码校验
@@ -325,7 +351,7 @@ public class AuthServiceImpl implements IAuthService {
         boolean bool = false;
         SpecialResVo currentSpecialRes = null;
         SpecialResVo mainSpecialRes = null;
-        for (SpecialResVo specialResVo:specialResVos) {
+        for (SpecialResVo specialResVo : specialResVos) {
             bool = specialResVo.getCode().equals(accessSpecial) || bool;
             currentSpecialRes = specialResVo.getCode().equals(accessSpecial) ? specialResVo : currentSpecialRes;
             mainSpecialRes = specialResVo.getIsMain() == NumberConstant.ONE ? specialResVo : mainSpecialRes;
@@ -345,9 +371,9 @@ public class AuthServiceImpl implements IAuthService {
         }
 
         //校验企业非专场时,Hearder中标识码是否为主平台标识码
-        if (StringUtils.isBlank(special) ) {
+        if (StringUtils.isBlank(special)) {
             if (currentSpecialRes != null && currentSpecialRes.getIsMain() != NumberConstant.ONE) {
-                String msg = "请进入"+ mainSpecialRes.getName() +",平台网站为"+ mainSpecialRes.getWebsite() +",如有疑问请联系平台系统管理员确认!";
+                String msg = "请进入" + mainSpecialRes.getName() + ",平台网站为" + mainSpecialRes.getWebsite() + ",如有疑问请联系平台系统管理员确认!";
                 return HttpResult.error(HttpStatus.PARAMETERS_MISSING_CODE, msg);
             }
         } else {
@@ -410,6 +436,300 @@ public class AuthServiceImpl implements IAuthService {
         return loginRes;
     }
 
+    private String generate(String accessToken, String openId) {
+        String secret = "aZmD4gExLbChg8XfqNDa"; // 自己的密钥(注意不要泄露)
+
+        // 模拟请求参数
+        Map<String, String> params = new HashMap<>();
+        params.put("access_token", accessToken);
+        params.put("openid", openId);
+
+        // 1️⃣ 参数名按字母顺序排序
+        List<String> keys = new ArrayList<>(params.keySet());
+        Collections.sort(keys);
+
+        // 2️⃣ 按 `key=value&key2=value2` 拼接字符串
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < keys.size(); i++) {
+            String key = keys.get(i);
+            sb.append(key).append("=").append(params.get(key));
+            if (i < keys.size() - 1) {
+                sb.append("&");
+            }
+        }
+        String signStr = sb.toString();
+
+        // 3️⃣ 使用 HMAC-SHA256 算法计算签名
+        Mac hmacSha256;
+        try {
+            hmacSha256 = Mac.getInstance("HmacSHA256");
+            SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
+            hmacSha256.init(secretKey);
+            byte[] hash = hmacSha256.doFinal(signStr.getBytes(StandardCharsets.UTF_8));
+            // 4️⃣ 转为十六进制字符串(与 Node.js 的 digest('hex') 对齐)
+            StringBuilder hex = new StringBuilder();
+            for (byte b : hash) {
+                String hexByte = Integer.toHexString(0xff & b);
+                if (hexByte.length() == 1) {
+                    hex.append('0');
+                }
+                hex.append(hexByte);
+            }
+            return hex.toString();
+        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
+            throw new RuntimeException("签名解析异常");
+        }
+
+    }
+
+    @Override
+    public HttpResult quickLogin(QuickLoginBase loginBase) {
+        if (StringUtils.isBlank(loginBase.getSystemType())) {
+            return HttpResult.error(HttpStatus.PARAMETERS_MISSING_CODE, "应用服务类型不能为空!");
+        }
+        if (StringUtils.isBlank(loginBase.getClientType())) {
+            return HttpResult.error(HttpStatus.PARAMETERS_MISSING_CODE, "客户端类型不能为空!");
+        }
+        if (StringUtils.isBlank(loginBase.getAccessSpecial()) && loginBase.getSystemType() != SystemTypeEnum.MANAGE.getCode()) {
+            return HttpResult.error(HttpStatus.PARAMETERS_MISSING_CODE, "平台标识不能为空!");
+        }
+        //云函数处理,获取手机号
+        String sign = generate(loginBase.getAccessToken(), loginBase.getOpenid());
+        String res = OkHttpUtils.builder().url("http://yjdl.sckaiwu.cn/oneClickLogin4").addPara("access_token", loginBase.getAccessToken()).addPara("openid", loginBase.getOpenid()).addPara("sign", sign).get().sync();
+        log.info("云函数请求结果:{}", res);
+        CloudFunDto cloudFunDto = JSONObject.parseObject(res, CloudFunDto.class);
+        if (!Objects.equals(cloudFunDto.getCode(), 0)) {
+            throw new RuntimeException("获取号码超时,请重试或使用账户密码登录");
+        }
+        String phone = cloudFunDto.getPhoneNumber();
+
+        /*查询用户信息**/
+        RDriverDetailVo driver = fleetService.findDriverDetai(phone);
+        /*信息校验**/
+        if (driver == null) {
+            return HttpResult.error(HttpStatus.QUERY_FAIL_CODE, "账号不存在,请检查并重新输入!");
+        }
+        if (driver.getStatus() == Global.YES) {
+            return HttpResult.error(HttpStatus.CODE_10301, "您的账号已冻结,如需帮助,请致电平台客服!");
+        }
+
+        //企业信息
+        EntCacheResDto enterprise = systemService.queryEntDetails(driver.getEntId());
+
+        /*生成token**/
+        String clientType = loginBase.getClientType();
+        Integer systemType = loginBase.getSystemType();
+        String special = loginBase.getAccessSpecial();
+        Map<String, Object> info = new HashMap<>(Global.NUMERICAL_SIXTEEN);
+        info.put("userId", driver.getId());
+        info.put("account", phone);
+        info.put("clientType", clientType);
+        info.put("systemType", systemType);
+        info.put("special", special);
+        String key = Global.getFullUserTokenKey(clientType, !systemType.equals(SystemTypeEnum.MANAGE.getCode()) ? special : null, driver.getId());
+        String token = EncryUtil.encryV1(Global.PRI_KEY, JSON.toJSONString(info));
+        int expireTime = ClientTypeEnum.expireTime(loginBase.getClientType());
+        RedissonUtils.putString(key, token, expireTime);
+
+        if (StringUtils.isBlank(token)) {
+            return HttpResult.error(HttpStatus.CODE_10301, "生成密钥失败,请联系系统管理员!");
+        }
+
+
+        /*缓存信息**/
+        LoginBase loginBase1 = new LoginBase();
+        loginBase1.setAccount(phone);
+        loginBase1.setPassword("");
+        loginBase1.setCaptcha("");
+        loginBase1.setSystemType(loginBase.getSystemType());
+        loginBase1.setClientType(loginBase.getClientType());
+        loginBase1.setAccessSpecial(loginBase.getAccessSpecial());
+        loginBase1.setLoginMethod(loginBase.getLoginMethod());
+
+
+        AsyncFactory.execute(new AsyncProcess(loginBase1, null, driver, enterprise, remoteUserService));
+
+        /*数据组装**/
+        LoginResVo1 loginRes = new LoginResVo1();
+        loginRes.setId(driver.getId());
+        loginRes.setName(driver.getName());
+        loginRes.setAccount(phone);
+        loginRes.setPhone(driver.getPhone());
+        loginRes.setStatus(driver.getStatus());
+        loginRes.setEntId(driver.getEntId());
+        loginRes.setFirmName(enterprise != null ? enterprise.getFirmName() : null);
+        loginRes.setApproval(enterprise != null ? enterprise.getApproval() : null);
+        loginRes.setEntTypeNames(enterprise != null ? enterprise.getEntTypeNames() : null);
+        loginRes.setClientType(loginBase.getClientType());
+        loginRes.setSystemType(loginBase.getSystemType());
+        loginRes.setToken(token);
+
+        //校验是否切换设备登录
+        String deviceId = loginBase.getDeviceId();
+        String deviceKey = String.format(RedisConstant.DEVICE_CODE, phone);
+        String deviceOld = (String) redisTemplate.opsForValue().get(deviceKey);
+        if (StrUtil.isBlank(deviceOld)) {
+            redisTemplate.opsForValue().set(deviceKey, deviceId);
+        } else {
+            if (!StrUtil.equals(deviceOld, deviceId)) {
+                log.error("用户切换设备登录:{} 旧设备id:{} 新设备id:{}", phone, deviceOld, deviceId);
+                String code = IdUtil.fastSimpleUUID();//获取二维码或者发送短信的编码
+                InfoDto infoDto = new InfoDto();
+                infoDto.setPhone(phone);
+                infoDto.setDeviceId(deviceId);
+                infoDto.setCity("");
+                redisTemplate.opsForValue().set(String.format(RedisConstant.LOGIN_TOKEN, code), infoDto, 10, TimeUnit.MINUTES);//10分钟
+
+                //记录登录成功的信息,后续二维码校验通过或着扫码通过返回信息 保存十分钟
+                redisTemplate.opsForValue().set(String.format(RedisConstant.LOGIN_CACHE, phone), loginRes, 10, TimeUnit.MINUTES);
+
+                return HttpResult.error(HttpStatus.ACCOUNT_DEVICE_CODE, HttpStatus.ACCOUNT_DEVICE_CODE_MESSAGE, code);
+            }
+        }
+//        String ipKey = String.format(RedisConstant.DEVICE_IP, phone);
+//        String cityOld = (String) redisTemplate.opsForValue().get(ipKey);
+//        //【矿拉拉】检测到您 的账户在异地登录,若非您本人操作,请及时修改密码保证账户安全
+//        if (StrUtil.isBlank(cityOld)) {
+//            //todo 计算新的IP归属地
+//            redisTemplate.opsForValue().set(ipKey, "");
+//        } else {
+//            //todo 计算新的IP归属地
+//            //归属地发生变化
+//            if (StrUtil.equals(cityOld, "")) {
+//                String code = IdUtil.fastSimpleUUID();//获取二维码或者发送短信的编码
+//                InfoDto infoDto = new InfoDto();
+//                infoDto.setPhone(phone);
+//                infoDto.setDeviceId(deviceId);
+//                infoDto.setCity("");
+//                redisTemplate.opsForValue().set(String.format(RedisConstant.LOGIN_TOKEN, code), infoDto, 10, TimeUnit.MINUTES);//10分钟
+//
+//                //记录登录成功的信息,后续二维码校验通过或着扫码通过返回信息 保存十分钟
+//                redisTemplate.opsForValue().set(String.format(RedisConstant.LOGIN_CACHE, phone), loginRes, 10, TimeUnit.MINUTES);
+//                return HttpResult.error(HttpStatus.ACCOUNT_DEVICE_CODE, HttpStatus.ACCOUNT_DEVICE_CODE_MESSAGE, code);
+//            }
+//        }
+        return HttpResult.ok(loginRes);
+    }
+
+    @Override
+    public HttpResult getQr(String token) {//新设备生成二维码,不需要登录
+        // 要生成的内容,比如登录凭证或校验码
+//        InfoDto infoDto1 = new InfoDto();
+//        infoDto1.setPhone("15902849627");
+//        infoDto1.setDeviceId("132");
+//        infoDto1.setCity("312");
+//
+//        redisTemplate.opsForValue().set(String.format(RedisConstant.LOGIN_TOKEN, token), infoDto1);
+        InfoDto infoDto = (InfoDto) redisTemplate.opsForValue().get(String.format(RedisConstant.LOGIN_TOKEN, token));
+        if (Objects.isNull(infoDto)) {
+            throw new RuntimeException("未认证或Token已到期,请重新登录!");//未进行过登录,或登录token已到期
+        }
+        //生成二维码的编码内容
+        String code = IdUtil.fastSimpleUUID();
+        redisTemplate.opsForValue().set(String.format(RedisConstant.LOGIN_QR_TOKEN_CACHE, infoDto.getPhone()), code);
+        BufferedImage image = QrCodeUtil.generate(code, QrConfig.create().setWidth(100).setHeight(100));
+        // 2. 转为 Base64
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            ImageIO.write(image, "png", baos);
+        } catch (IOException e) {
+            throw new RuntimeException("二维码生成异常,请稍后再试!");
+        }
+        byte[] bytes = baos.toByteArray();
+        String base64 = Base64Encoder.encode(bytes);
+
+        // 3. 返回前端(前端直接拼接 data:image/png;base64 即可)
+        return HttpResult.ok("获取成功", "data:image/png;base64," + base64);
+    }
+
+    /**
+     * @param code 二维码内容
+     * @return
+     */
+    @Override
+    public HttpResult checkQr(String code) {//旧设备扫码,校验二维码。需要登录
+        LoginUserInfo loginUserInfo = LoginUserHolder.get();
+        if (Objects.isNull(loginUserInfo)) {
+            throw new RuntimeException("未登录");
+        }
+        String codeOld = (String) redisTemplate.opsForValue().get(String.format(RedisConstant.LOGIN_QR_TOKEN_CACHE, loginUserInfo.getPhone()));
+        if (!StrUtil.equals(code, codeOld)) {
+            throw new RuntimeException("二维码已失效或不存在");
+        }
+        //校验通过
+        redisTemplate.opsForValue().set(String.format(RedisConstant.LOGIN_QR_CHECK_CACHE, loginUserInfo.getPhone()), loginUserInfo.getPhone(), 3, TimeUnit.MINUTES);
+        return HttpResult.ok("校验通过");
+    }
+
+    @Override
+    public HttpResult getQrCheckResult(String token) {//新设备轮询,获取是否扫码的结果。不需要登录
+        InfoDto infoDto = (InfoDto) redisTemplate.opsForValue().get(String.format(RedisConstant.LOGIN_TOKEN, token));
+        if (Objects.isNull(infoDto)) {
+            return HttpResult.error("未认证或Token已到期,请重新登录!");
+        }
+        String phone = infoDto.getPhone();
+        String deviceId = infoDto.getDeviceId();
+        String key = String.format(RedisConstant.LOGIN_QR_CHECK_CACHE, phone);
+        Object o = redisTemplate.opsForValue().get(key);
+        if (Objects.nonNull(o)) {
+            redisTemplate.delete(key);
+            redisTemplate.opsForValue().set(String.format(RedisConstant.DEVICE_CODE, phone), deviceId);//登录成功,记录新设备id
+            LoginResVo1 res = (LoginResVo1) redisTemplate.opsForValue().get(String.format(RedisConstant.LOGIN_CACHE, phone));
+            return HttpResult.ok("登录成功", res);
+        }
+        return HttpResult.error("暂未扫码确认");
+    }
+
+    @Override
+    public HttpResult getSms(String token) {//不需要登录
+        // 要生成的内容,比如登录凭证或校验码
+        InfoDto infoDto = (InfoDto) redisTemplate.opsForValue().get(String.format(RedisConstant.LOGIN_TOKEN, token));
+        if (Objects.isNull(infoDto)) {
+            throw new RuntimeException("未认证或Token已到期,请重新登录!");//未进行过登录,或登录token已到期
+        }
+
+        String type = "yd";
+        String phone = infoDto.getPhone();
+
+        Map<String, Object> params = new HashMap<>(Global.NUMERICAL_SIXTEEN);
+        //生产环境才发短信
+        String code = NumberUtils.createRandomVcode();
+        int time = 3;
+        params.put("code", code);
+        params.put("time", time);
+        SckwSms sckwSms = new SckwSms();
+        sckwSms.setPhone(phone).setType(type)
+                .setTemplateCode(SmsCodeEnum.VERIFICATION_CODE2)
+                .setParams(params).setCreateBy(LoginUserHolder.getUserId());
+        streamBridge.send("sckw-sms", com.alibaba.fastjson2.JSON.toJSONString(sckwSms));
+        //
+        String key2 = String.format(RedisConstant.LOGIN_YZM_TOKEN_CACHE, token);
+        redisTemplate.opsForValue().set(key2, code, time, TimeUnit.MINUTES);
+        return HttpResult.ok("发送成功");
+    }
+
+    @Override
+    public HttpResult checkSms(String token, String code) {
+        InfoDto infoDto = (InfoDto) redisTemplate.opsForValue().get(String.format(RedisConstant.LOGIN_TOKEN, token));
+        if (Objects.isNull(infoDto)) {
+            throw new RuntimeException("未认证或Token已到期,请重新登录!");//未进行过登录,或登录token已到期
+        }
+
+        String key = String.format(RedisConstant.LOGIN_YZM_TOKEN_CACHE, token);
+        String codeOld = (String) redisTemplate.opsForValue().get(key);
+        if (StrUtil.isBlank(codeOld)) {
+            throw new RuntimeException("验证码已失效");
+        }
+        if (!StrUtil.equals(codeOld, code)) {
+            redisTemplate.delete(key);
+            throw new RuntimeException("验证码不正确");
+        }
+        String phone = infoDto.getPhone();
+        redisTemplate.opsForValue().set(String.format(RedisConstant.DEVICE_CODE, phone), infoDto.getDeviceId());//登录成功,记录新设备id
+        LoginResVo1 res = (LoginResVo1) redisTemplate.opsForValue().get(String.format(RedisConstant.LOGIN_CACHE, phone));
+        return HttpResult.ok("登录成功", res);
+    }
+
     /**
      * @param loginBase {}
      * @param userId    用户ID
@@ -448,8 +768,7 @@ public class AuthServiceImpl implements IAuthService {
 
         private final RemoteUserService remoteUserService;
 
-        public AsyncProcess(LoginBase loginBase, KwsUserResDto user, RDriverDetailVo driver, EntCacheResDto enterprise,
-                             RemoteUserService remoteUserService) {
+        public AsyncProcess(LoginBase loginBase, KwsUserResDto user, RDriverDetailVo driver, EntCacheResDto enterprise, RemoteUserService remoteUserService) {
             this.loginBase = loginBase;
             this.user = user;
             this.driver = driver;
@@ -581,7 +900,7 @@ public class AuthServiceImpl implements IAuthService {
                     enterpriseIds = enterpriseIds.stream().distinct().collect(Collectors.toList());
                     String key = Global.getCustomerManagerUserLoginKey(loginUserInfo.getSystemType(), loginUserInfo.getId());
                     RSet<Object> set = RedissonUtils.getSet(key);
-                    if (CollectionUtils.isNotEmpty(set)){
+                    if (CollectionUtils.isNotEmpty(set)) {
                         RedissonUtils.delete(key);
                     }
                     RedissonUtils.putSet(key, enterpriseIds);

+ 4 - 2
sckw-common/sckw-common-core/src/main/java/com/sckw/core/model/enums/LoginMethodEnum.java

@@ -8,7 +8,9 @@ package com.sckw.core.model.enums;
 public enum LoginMethodEnum {
     ORDINARY(1, "账号密码登录"),
     SMS(2,  "短信登录"),
-    ACCOUNT(3,  "账号登录");
+    ACCOUNT(3,  "账号登录"),
+    QUICK(4,  "快速登录")
+    ;
 
     private int value;
 
@@ -41,4 +43,4 @@ public enum LoginMethodEnum {
     public void setName(String name) {
         this.name = name;
     }
-}
+}

+ 90 - 31
sckw-common/sckw-common-core/src/main/java/com/sckw/core/web/constant/HttpStatus.java

@@ -7,34 +7,54 @@ package com.sckw.core.web.constant;
  */
 public class HttpStatus {
 
-    /**成功状态码*/
+    /**
+     * 成功状态码
+     */
     public static final int SUCCESS_CODE = 60200;
 
-    /**成功提示信息*/
+    /**
+     * 成功提示信息
+     */
     public static final String SUCCESS_MESSAGE = "success";
 
-    /**未登录状态码*/
+    /**
+     * 未登录状态码
+     */
     public static final int UN_LOGIN_CODE = 60300;
-    /**未登录提示信息*/
+    /**
+     * 未登录提示信息
+     */
     public static final String UN_LOGIN_MESSAGE = "您尚未登录,请先登录!";
 
-    /**访问权限状态码*/
+    /**
+     * 访问权限状态码
+     */
     public static final int AUTHORITY_NO_CODE = 60403;
     public static final String ACCESS_FIAL = "暂无该功能权限!";
 
-    /**全局异常状态码*/
+    /**
+     * 全局异常状态码
+     */
     public static final int GLOBAL_EXCEPTION_CODE = 60500;
 
-    /**完结贸易订单失败异常码*/
+    /**
+     * 完结贸易订单失败异常码
+     */
     public static final int COMPLETE_TORDER_FAIL_CODE = 60666;
 
-    /** 商品上架失败异常码*/
+    /**
+     * 商品上架失败异常码
+     */
     public static final int GOODS_PUT_ON_SHELVES_FAIL_CODE = 60667;
 
-    /**全局异常提示信息*/
+    /**
+     * 全局异常提示信息
+     */
     public static final String GLOBAL_EXCEPTION_MESSAGE = "攻城狮正在拼命优化,请您稍候再试!";
 
-    /**参数缺失状态码*/
+    /**
+     * 参数缺失状态码
+     */
     public static final int PARAMETERS_MISSING_CODE = 60600;
     public static final String ID_MISSING = "id不能为空!";
     public static final String ACCOUNT_MISSING = "用户账号必填!";
@@ -47,21 +67,24 @@ public class HttpStatus {
     public static final String ADDRESS_EXISTS = "地点已存在,不可重复!";
     public static final String INVALID_REQUEST = "无效的接口请求!";
 
-    /**其他自定义状态码*/
+    /**
+     * 其他自定义状态码
+     */
     public static final int CODE_60603 = 60603;
     public static final String ENTCERTIFICATES_INVAILD = "您的企业资质已失效,暂没有权限访问,请尽快更新资质";
     public static final String ENTCERTIFICATES_NOT_REGISTER = "您未做企业资质认证,暂没有权限访问";
     public static final String ENTCERTIFICATES_NOT_PASS = "您的企业资质认证还在审核中,暂没有权限访问";
 
 
-
     public static final int CODE_60604 = 60604;
     public static final int CODE_60605 = 60605;
     public static final int CODE_60606 = 60606;
     public static final int CODE_60607 = 60607;
     public static final int CODE_60608 = 60608;
     public static final int CODE_60609 = 60609;
-    /**sentinel异常code定义*/
+    /**
+     * sentinel异常code定义
+     */
     public static final int CODE_60701 = 60701;
     public static final int CODE_60801 = 60801;
     public static final int CODE_60901 = 60901;
@@ -76,15 +99,23 @@ public class HttpStatus {
     public static final String AUTHORITY_EXCEPTION_ERROR_MESSAGE = "授权规则检测不同,请检查访问参数";
     public static final String OTHER_EXCEPTION_ERROR_MESSAGE = "非法访问,请稍后重试";
 
-    /**版本号和接口版本不对称状态码*/
+    /**
+     * 版本号和接口版本不对称状态码
+     */
     public static final int VERSION_NOT_NEWEST_CODE = 60700;
-    /**版本号和接口版本不对称提示信息*/
+    /**
+     * 版本号和接口版本不对称提示信息
+     */
     public static final String VERSION_NOT_NEWEST_MESSAGE = "当前版本较低,请更新升级后再试!";
 
 
-    /**参数格式不正确状态码*/
+    /**
+     * 参数格式不正确状态码
+     */
     public static final int PARAMETERS_PATTERN_ERROR_CODE = 60800;
-    /**参数格式不正确提示信息*/
+    /**
+     * 参数格式不正确提示信息
+     */
     public static final String PARAMETERS_PATTERN_ERROR_MESSAGE = "参数格式不正确";
     public static final String CONTACTS_ERROR = "联系人格式不正确";
     public static final String CONTACTS_PHONE_ERROR = "联系人手机号格式不正确";
@@ -93,31 +124,53 @@ public class HttpStatus {
     public static final String LEGAL_ID_CARD_ERROR = "法人身份证号格式不正确";
     public static final String ENT_CODE_ERROR = "营业执照编号格式不正确";
 
-    /**账号在别处登录状态码*/
+    /**
+     * 账号在别处登录状态码
+     */
     public static final int ACCOUNT_OTHER_LOGIN_CODE = 60900;
-    /**账号在别处登录提示信息*/
+    /**
+     * 账号在别处登录提示信息
+     */
     public static final String ACCOUNT_OTHER_LOGIN_MESSAGE = "您的账号已在其他设备登录,如非本人操作,请及时修改密码!";
 
 
-    /**token无效状态码*/
+    /**
+     * token无效状态码
+     */
     public static final int TOKEN_INVALID_CODE = 60901;
-    /**token无效提示信息*/
+    /**
+     * token无效提示信息
+     */
     public static final String TOKEN_INVALID_MESSAGE = "由于您一段时间未操作,为了您的账户安全,请重新登录!";
 
-    /**请求超过次数*/
+    /**
+     * 请求超过次数
+     */
     public static final int FREQUENCY_OUT = 60902;
-    /**请求超过次数提示信息*/
+    /**
+     * 请求超过次数提示信息
+     */
     public static final String FREQUENCY_OUT_MESSAGE = "您的操作过于频繁,请刷新浏览器或稍候重试!";
 
-    /**审核状态状态码*/
+    /**
+     * 审核状态状态码
+     */
     public static final int ACCOUNT_AUDIT_CODE = 60903;
-    /**审核状态状提示信息*/
+    /**
+     * 审核状态状提示信息
+     */
     public static final String ACCOUNT_AUDIT_MESSAGE = "您所属企业企业资质审批未通过,请核实确认!";
 
-    /**微信账号未绑定态码*/
+    /**
+     * 微信账号未绑定态码
+     */
     public static final int WECHAR_BIND_CODE = 60904;
-    /**微信账号未绑定提示信息*/
+    /**
+     * 微信账号未绑定提示信息
+     */
     public static final String WECHAR_BIND_MESSAGE = "您的微信还未绑定危品汇账号!";
+    public static final int ACCOUNT_DEVICE_CODE = 60905;
+    public static final String ACCOUNT_DEVICE_CODE_MESSAGE = "检测到您的账户使用新设备登录,为保障您账户安全,需选择一种方式进行验证";
 
     /**
      * 自定义状态码,该状态码没有特殊含义,只是提供一个状态标识(目前提供9个,可自行扩展)
@@ -125,13 +178,17 @@ public class HttpStatus {
      */
     public static final int CODE_10301 = 10301;
 
-    /**数据库的操作失败*/
+    /**
+     * 数据库的操作失败
+     */
     public static final int CRUD_FAIL_CODE = 60601;
     public static final String UPDATE_FAIL = "更新失败";
     public static final String INSERT_FAIL = "新增失败";
     public static final String DELETE_FAIL = "删除失败";
 
-    /**未查询到相关信息*/
+    /**
+     * 未查询到相关信息
+     */
     public static final int QUERY_FAIL_CODE = 60602;
     public static final String ACCOUNT_NOT_EXISTS = "用户信息不存在或已失效";
     public static final String ENT_NOT_EXISTS = "企业信息不存在或已失效";
@@ -149,7 +206,9 @@ public class HttpStatus {
     public static final String CONTRACT_NOT_EXISTS = "未查询到合同或已失效";
     public static final String BANNER_NOT_EXISTS = "未查询到banner数据或已失效";
 
-    /**自定义提示消息*/
+    /**
+     * 自定义提示消息
+     */
     public static final String PASSWD_ERROR = "密码不正确";
     public static final String PASSWD_REPEAT = "新密码与旧密码不能一样!";
     public static final String CAPTCHA_ERROR = "验证码输入错误";
@@ -195,4 +254,4 @@ public class HttpStatus {
     public static final String DICT_EXISTS = "字典键值已存在,不可重复!";
     public static final String PL34 = "3PL和4PL不能同时注册!";
 
-}
+}

+ 16 - 0
sckw-common/sckw-common-redis/src/main/java/com/sckw/redis/constant/RedisConstant.java

@@ -122,5 +122,21 @@ public class RedisConstant {
      * uid:filter:channel->time
      */
     public static final String WALLET_TIME = "%s:%s:%s";
+    //设备序列号存储
+    public static final String DEVICE_CODE = "device:code:%s";
+    //设备ip存储
+    public static final String DEVICE_IP = "device:ip:%s";
+
+    public static final String LOGIN_CACHE = "login:cache:%s";
+    /**
+     * 登陆后,保存生成的token,用来换取二维码或手机验证码
+     */
+    public static final String LOGIN_TOKEN = "login:token:%s";
+    //生成的二维码缓存
+    public static final String LOGIN_QR_TOKEN_CACHE = "login:cache:qr:%s";
+    public static final String LOGIN_QR_CHECK_CACHE = "login:qr:check:%s";
+
+    //生成的验证码缓存
+    public static final String LOGIN_YZM_TOKEN_CACHE = "login:cache:yzm:%s";
 
 }