Pārlūkot izejas kodu

添加微信一键登录接口

ximinghao 3 mēneši atpakaļ
vecāks
revīzija
f4188b2dd2

+ 1 - 0
.idea/dictionaries/project.xml

@@ -3,6 +3,7 @@
     <words>
       <w>dwithin</w>
       <w>gsqy</w>
+      <w>xjcy</w>
     </words>
   </dictionary>
 </component>

+ 4 - 4
src/main/java/com/skyversation/xjcy/Timer.java

@@ -53,9 +53,9 @@ public class Timer {
         LocalDateTime actuallyTime = now.minusDays(1).atTime(23,59,59,9999);
 //        LocalDateTime actuallyTime = LocalDateTime.of(2025,9,30,23,59,59,9999);
 
-        List<Enterprise> enterprises = dMSService.query(authService.getToken(), DMSQuery.ENTERPRISE, Enterprise.class,actuallyTime.toLocalDate());
-        List<LeaseDetail> leaseDetails = dMSService.query(authService.getToken(), DMSQuery.LAST_LEASE_DETAIL, LeaseDetail.class,actuallyTime.toLocalDate());
-        List<EnterpriseEconomic> enterpriseEconomics = dMSService.query(authService.getToken(), DMSQuery.ECONOMIC, EnterpriseEconomic.class,actuallyTime.toLocalDate());
+        List<Enterprise> enterprises = dMSService.query(authService.getTokenOfServiceAccount(), DMSQuery.ENTERPRISE, Enterprise.class,actuallyTime.toLocalDate());
+        List<LeaseDetail> leaseDetails = dMSService.query(authService.getTokenOfServiceAccount(), DMSQuery.LAST_LEASE_DETAIL, LeaseDetail.class,actuallyTime.toLocalDate());
+        List<EnterpriseEconomic> enterpriseEconomics = dMSService.query(authService.getTokenOfServiceAccount(), DMSQuery.ECONOMIC, EnterpriseEconomic.class,actuallyTime.toLocalDate());
 
         EnterpriseHealthComputer enterpriseHealthComputer = new EnterpriseHealthComputer(enterprises);
 
@@ -81,7 +81,7 @@ public class Timer {
 
         List<JSONObject> results = enterpriseHealthComputer.getResultJsons();
         for (JSONObject result : results) {
-            boolean success = dMSService.insertToDms(result, authService.getToken(), DMSColumn.ENTERPRISE_HEALTH);
+            boolean success = dMSService.insertToDms(result, authService.getTokenOfServiceAccount(), DMSColumn.ENTERPRISE_HEALTH);
             if (!success) {
                 System.out.println("保存失败");
             }

+ 39 - 0
src/main/java/com/skyversation/xjcy/controller/LoginController.java

@@ -0,0 +1,39 @@
+package com.skyversation.xjcy.controller;
+
+
+import com.skyversation.xjcy.oauth.AuthService;
+import com.skyversation.xjcy.service.SerialNumberGenerator;
+import com.skyversation.xjcy.util.MessageManage;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@Slf4j
+@Validated
+@RestController
+@RequestMapping("/login")
+public class LoginController {
+
+
+    private final AuthService authService;
+
+    public LoginController(AuthService authService) {
+        this.authService = authService;
+    }
+
+    @RequestMapping(value = "/wechat", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    public String serialNumber(@RequestParam(required = false)String jsCode) {
+        try {
+            if (jsCode == null) {
+                return MessageManage.getInstance().getResultContent(-1,"缺失jsCode","缺失jsCode");
+            }
+            return authService.logOrRegWxAccount(jsCode);
+        } catch (Exception e) {
+            return MessageManage.getInstance().getResultContent(500, e.getMessage(), "未知错误");
+        }
+
+    }
+}

+ 3 - 3
src/main/java/com/skyversation/xjcy/controller/UploadController.java

@@ -58,7 +58,7 @@ public class UploadController {
         try {
             pdfPath = dMSService.uploadFile(
                     FileUtil.getResource(pdf),
-                    authService.getToken(),
+                    authService.getTokenOfServiceAccount(),
                     DMSColumn.WECHAT_ARTICLE,
                     "c_article_content",
                     FileType.FILE);
@@ -69,7 +69,7 @@ public class UploadController {
         try {
             picturePath = dMSService.uploadFile(
                     FileUtil.getResource(picture),
-                    authService.getToken(),
+                    authService.getTokenOfServiceAccount(),
                     DMSColumn.WECHAT_ARTICLE,
                     "c_article_picture",
                     FileType.PICTURE);
@@ -87,7 +87,7 @@ public class UploadController {
 
         obj.put("title", title.replaceAll("'", "''"));
         obj.put("content", title.replaceAll("'", "''"));
-        boolean success = dMSService.insertToDms(obj, authService.getToken(), DMSColumn.WECHAT_ARTICLE);
+        boolean success = dMSService.insertToDms(obj, authService.getTokenOfServiceAccount(), DMSColumn.WECHAT_ARTICLE);
         if (success) {
             return MessageManage.getInstance().getResultContent(200, "完成", "完成");
         } else {

+ 130 - 16
src/main/java/com/skyversation/xjcy/oauth/AuthService.java

@@ -3,12 +3,15 @@ package com.skyversation.xjcy.oauth;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.skyversation.xjcy.util.HttpUtil;
+import com.skyversation.xjcy.util.MessageManage;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.MultiValueMap;
 import org.springframework.util.StringUtils;
 
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -16,6 +19,7 @@ import java.util.Objects;
 @Service
 public class AuthService {
 
+    private final WxAuthService wxAuthService;
     @Value("${app.oauth.login-name}")
     private String loginName;
     @Value("${app.oauth.password}")
@@ -25,38 +29,148 @@ public class AuthService {
 
     private String cacheToken;
 
-    private String loginForToken() throws RuntimeException {
+
+    public AuthService(WxAuthService wxAuthService) {
+        this.wxAuthService = wxAuthService;
+    }
+
+    private String loginForToken(String loginName, String password) throws RuntimeException {
+        JSONObject jsonObject = login(loginName, password);
+        if ("密码错误".equals(jsonObject.getString("message"))) {
+            throw new RuntimeException("请检查使用的账户和密码是否正确");
+        }
+        if (!Objects.equals(jsonObject.getString("code"), "200")) {
+            throw new RuntimeException(jsonObject.getString("message"));
+        }
+        return jsonObject.getString("message");
+    }
+
+    private JSONObject login(String loginName, String password) {
         if (!StringUtils.hasText(loginName) || !StringUtils.hasText(password)) {
             throw new RuntimeException("请提供用于访问DMS的账号密码");
         }
         MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
+        params.add("clientId", "2");
         params.add("userName", loginName);
         params.add("password", password);
-        params.add("clientId", "2");
-        String response = HttpUtil.requestPost(oauthPath+"/api/user/login", params, new HashMap<>());
-        JSONObject jsonObject = JSON.parseObject(response);
-        if ("密码错误".equals(jsonObject.getString("message"))) {
-            loginName = null;
-            password = null;
-            throw new RuntimeException("请检查使用的账户和密码是否正确,已停止使用该账号密码以避免锁定账号");
-        }
-        if (!Objects.equals(jsonObject.getString("code"), "200")) {
-            throw new RuntimeException(jsonObject.getString("message"));
+        String response = HttpUtil.requestPost(oauthPath + "/api/user/login", params, new HashMap<>());
+        return JSON.parseObject(response);
+    }
+
+    private JSONObject register(String loginName, String password) {
+        if (!StringUtils.hasText(loginName) || !StringUtils.hasText(password)) {
+            throw new RuntimeException("请提供用于访问DMS的账号密码");
         }
-        return jsonObject.getString("message");
+        MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
+        params.add("userName", loginName);
+        params.add("password", password);
+        String response = HttpUtil.requestPost(oauthPath + "/user/register", params, new HashMap<>());
+        return JSON.parseObject(response);
     }
-    public boolean checkToken(String token){
+
+    private void initUser(int userId) {
+        //TODO 初始化用户
+    }
+
+    /**
+     * 检查token有效性
+     *
+     * @param token oauth系统的token
+     * @return 是否有效
+     */
+    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+    public boolean checkToken(String token) {
         Map<String, String> header = new HashMap<>();
         header.put("token", token);
-        String response = HttpUtil.requestGet(oauthPath+"/user/validateToken",null,header);
+        String response = HttpUtil.requestGet(oauthPath + "/user/validateToken", null, header);
         JSONObject jsonObject = JSON.parseObject(response);
         Integer code = jsonObject.getInteger("code");
         return Integer.valueOf(200).equals(code);
     }
-    public String getToken() throws RuntimeException {
+
+    /**
+     * 获取服务器内置的账户token
+     *
+     * @return 服务器内置账户的token
+     * @throws RuntimeException 内置账户信息异常
+     */
+    public String getTokenOfServiceAccount() throws RuntimeException {
         if (!StringUtils.hasText(cacheToken) || !checkToken(cacheToken)) {
-            cacheToken = loginForToken();
+            cacheToken = loginForToken(this.loginName, this.password);
         }
         return cacheToken;
     }
+
+    /**
+     * 通过wx的一键登录登录到oauth。
+     * 实际上每个wx用户都会被分配一个oauth账户,此账户以用户名与wx的openid建立连接。
+     * 逻辑上这个方法会尝试登录,无法登录时尝试建立oauth账户
+     *
+     * @param wxCode wx.login()方法提供的一键登录用code
+     * @return 完整的response.body
+     */
+    public String logOrRegWxAccount(String wxCode) {
+        WxAuthService.Result wxResult = wxAuthService.wxLogin(wxCode);
+        if (wxResult.isSuccess()) {
+            //生成期望的绑定账户和密码
+            String oauthAccount = "#wx" + wxResult.getWxOpenId();
+            String oauthPassword = "Wx@" + encrypt(wxResult.getWxOpenId() + "chatWe");
+
+            //尝试登录
+            JSONObject result = login(oauthAccount, oauthPassword);
+            String message = result.getString("message");
+            Integer code = result.getInteger("code");
+            if (Integer.valueOf(200).equals(code)) {
+                return message;
+            } else {
+                //登录失败
+                if ("用户不存在".equals(message)) {
+                    //尝试注册
+                    JSONObject regResult = register(oauthAccount, oauthPassword);
+                    if (!Integer.valueOf(200).equals(regResult.getInteger("code"))) {
+                        return MessageManage.getInstance().getResultContent(-1, "注册失败,未知异常", "注册失败,未知异常");
+                    }
+                    JSONObject userUnInitResult = login(oauthAccount, oauthPassword);
+                    if (!Integer.valueOf(200).equals(userUnInitResult.getInteger("code"))) {
+                        return MessageManage.getInstance().getResultContent(-1, "注册失败,未知异常", "注册失败,未知异常");
+                    }
+                    Integer userId = userUnInitResult.getJSONObject("content").getInteger("id");
+                    if (userId != null) {
+                        initUser(userId);
+                    }
+                    return login(oauthAccount, oauthPassword).toJSONString();
+                } else {
+                    return MessageManage.getInstance().getResultContent(-1, "未知异常", "未知异常");
+                }
+            }
+        } else {
+            return MessageManage.getInstance().getResultContent(-1, wxResult.getError(), wxResult.getError());
+        }
+    }
+
+
+    /**
+     * 简单sha-256加密,不加盐
+     */
+    public static String encrypt(String input) {
+        try {
+            MessageDigest digest = MessageDigest.getInstance("SHA-256");
+            byte[] hash = digest.digest(input.getBytes());
+
+            // 将字节数组转换为十六进制字符串
+            StringBuilder hexString = new StringBuilder();
+            for (byte b : hash) {
+                String hex = Integer.toHexString(0xff & b);
+                if (hex.length() == 1) {
+                    hexString.append('0');
+                }
+                hexString.append(hex);
+            }
+            return hexString.toString();
+
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
 }

+ 60 - 0
src/main/java/com/skyversation/xjcy/oauth/WxAuthService.java

@@ -0,0 +1,60 @@
+package com.skyversation.xjcy.oauth;
+
+import com.alibaba.fastjson.JSONObject;
+import com.skyversation.xjcy.util.HttpUtil;
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+
+@SuppressWarnings("SpellCheckingInspection")
+@Service
+public class WxAuthService {
+    @Getter
+    public static class Result {
+        private boolean success;
+        private String error;
+        private String wxOpenId;
+        private String wxSessionKey;
+    }
+    @Value("${app.wechat.secret-key}")
+    private String secretKey;
+    @Value("${app.wechat.appid}")
+    private String appid;
+    private static final String WX_AUTH_PATH = "https://api.weixin.qq.com/sns/jscode2session";
+
+    public Result wxLogin(String code) {
+        MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
+        params.add("appid", appid);
+        params.add("secret", secretKey);
+        params.add("js_code", code);
+        params.add("grant_type", "authorization_code");
+        String body = HttpUtil.requestGet(WX_AUTH_PATH, params, null);
+        try {
+            JSONObject jsonObject = JSONObject.parseObject(body);
+            String openId = jsonObject.getString("openid");
+            String sessionKey = jsonObject.getString("session_key");
+            Integer errcode = jsonObject.getInteger("errcode");
+            if (errcode == 0) {
+                Result result = new Result();
+                result.wxSessionKey = sessionKey;
+                result.wxOpenId = openId;
+                result.success = true;
+                return result;
+            }else {
+                Result result = new Result();
+                if (errcode ==40029){
+                    result.error = "提供的code无效";
+                }else {
+                    result.error = "微信鉴权异常"+errcode;
+                }
+                return result;
+            }
+        } catch (NullPointerException e) {
+            Result result = new Result();
+            result.error = "无法访问wx鉴权接口";
+            return result;
+        }
+    }
+}

+ 6 - 6
src/main/java/com/skyversation/xjcy/service/DataCountService.java

@@ -119,12 +119,12 @@ public class DataCountService {
         LocalDate now = LocalDate.now();
 
         //数据量大不了所以不做流式
-        List<IndustrialPark> allIndustrialPark = dmsService.query(authService.getToken(), DMSQuery.INDUSTRIAL_PARK, IndustrialPark.class, now);
-        List<Enterprise> allEnterprise = dmsService.query(authService.getToken(), DMSQuery.ENTERPRISE, Enterprise.class, now);
-        List<EnterpriseEconomic> inDateEnterpriseEconomic = dmsService.query(authService.getToken(), DMSQuery.ECONOMIC_ALL_INDEED, EnterpriseEconomic.class, now);
-        List<Order> allOrder = dmsService.query(authService.getToken(), DMSQuery.ORDER, Order.class, now);
-        List<LeaseDetail> lastLeaseDetail = dmsService.query(authService.getToken(), DMSQuery.LAST_LEASE_DETAIL, LeaseDetail.class, now);
-        List<LeaseDetail> lastYearLease = dmsService.query(authService.getToken(), DMSQuery.LAST_YEAR_LEASE_DETAIL, LeaseDetail.class, now);
+        List<IndustrialPark> allIndustrialPark = dmsService.query(authService.getTokenOfServiceAccount(), DMSQuery.INDUSTRIAL_PARK, IndustrialPark.class, now);
+        List<Enterprise> allEnterprise = dmsService.query(authService.getTokenOfServiceAccount(), DMSQuery.ENTERPRISE, Enterprise.class, now);
+        List<EnterpriseEconomic> inDateEnterpriseEconomic = dmsService.query(authService.getTokenOfServiceAccount(), DMSQuery.ECONOMIC_ALL_INDEED, EnterpriseEconomic.class, now);
+        List<Order> allOrder = dmsService.query(authService.getTokenOfServiceAccount(), DMSQuery.ORDER, Order.class, now);
+        List<LeaseDetail> lastLeaseDetail = dmsService.query(authService.getTokenOfServiceAccount(), DMSQuery.LAST_LEASE_DETAIL, LeaseDetail.class, now);
+        List<LeaseDetail> lastYearLease = dmsService.query(authService.getTokenOfServiceAccount(), DMSQuery.LAST_YEAR_LEASE_DETAIL, LeaseDetail.class, now);
         //处理一下拿到的数据,建一建索引
         Map<String, List<EnterpriseEconomic>> enterpriseEconomicMap = new HashMap<>();
         inDateEnterpriseEconomic.forEach(e -> {

+ 2 - 2
src/main/java/com/skyversation/xjcy/service/DataExportService.java

@@ -32,8 +32,8 @@ public class DataExportService {
     public List<Map<String, Object>> exportClue(String c_clue_name, String c_enterprise_name, String construction_method, String timeStart, String timeEnd) {
         List<Map<String,Object>> result = new ArrayList<>();
         LocalDate now = LocalDate.now();
-        List<JSONObject> clues = dmsService.queryBeautifiedClue(authService.getToken(), c_clue_name,c_enterprise_name, construction_method,timeStart,timeEnd);
-        List<ClueFollow> clueFollows = dmsService.query(authService.getToken(), DMSQuery.CLUE_FOLLOW, ClueFollow.class,now);
+        List<JSONObject> clues = dmsService.queryBeautifiedClue(authService.getTokenOfServiceAccount(), c_clue_name,c_enterprise_name, construction_method,timeStart,timeEnd);
+        List<ClueFollow> clueFollows = dmsService.query(authService.getTokenOfServiceAccount(), DMSQuery.CLUE_FOLLOW, ClueFollow.class,now);
         //做map,仅留最近
         Map<String,ClueFollow> newestClueFollows = clueFollows.stream()
                 .filter(f->f.getCClueCode()!=null)

+ 3 - 3
src/main/java/com/skyversation/xjcy/service/InvestmentPreCountService.java

@@ -22,11 +22,11 @@ public class InvestmentPreCountService {
     public List<JSONObject> updateAllTarget() {
         synchronized (InvestmentPreCounter.LOCK) {
             LocalDate now = LocalDate.now();
-            List<JSONObject> allTarget = dmsService.getAllInvestmentTarget(authService.getToken());
-            List<Enterprise> allEnterprise = dmsService.query(authService.getToken(), DMSQuery.ENTERPRISE,Enterprise.class,now);
+            List<JSONObject> allTarget = dmsService.getAllInvestmentTarget(authService.getTokenOfServiceAccount());
+            List<Enterprise> allEnterprise = dmsService.query(authService.getTokenOfServiceAccount(), DMSQuery.ENTERPRISE,Enterprise.class,now);
             InvestmentPreCounter preCounter = new InvestmentPreCounter(allTarget, allEnterprise);
             preCounter.run();
-            dmsService.updateToDms(preCounter.getTargetNeedUpload(), authService.getToken(), DMSColumn.INVESTMENT_TARGET);
+            dmsService.updateToDms(preCounter.getTargetNeedUpload(), authService.getTokenOfServiceAccount(), DMSColumn.INVESTMENT_TARGET);
             return allTarget;
         }
     }

+ 7 - 7
src/main/java/com/skyversation/xjcy/service/ParkPreCountService.java

@@ -38,24 +38,24 @@ public class ParkPreCountService {
 
     public void updateAllRoom() {
         synchronized (ParkPreCounter.LOCK) {
-            List<JSONObject> allPark = dmsService.getAllPark(authService.getToken());
-            List<JSONObject> allLeaseByRooms = dmsService.getAllLease(authService.getToken());
+            List<JSONObject> allPark = dmsService.getAllPark(authService.getTokenOfServiceAccount());
+            List<JSONObject> allLeaseByRooms = dmsService.getAllLease(authService.getTokenOfServiceAccount());
             LocalDate now = LocalDate.now();
             ParkPreCounter parkPreCounter = new ParkPreCounter(allPark, allLeaseByRooms, now);
             parkPreCounter.run();
-            dmsService.updateToDms(parkPreCounter.getParkNeedUpload(), authService.getToken(), DMSColumn.INDUSTRIAL_PARK);
-            dmsService.updateToDms(parkPreCounter.getLeaseNeedUpload(), authService.getToken(), DMSColumn.LEASE_DETAIL);
+            dmsService.updateToDms(parkPreCounter.getParkNeedUpload(), authService.getTokenOfServiceAccount(), DMSColumn.INDUSTRIAL_PARK);
+            dmsService.updateToDms(parkPreCounter.getLeaseNeedUpload(), authService.getTokenOfServiceAccount(), DMSColumn.LEASE_DETAIL);
         }
     }
 
     public void updateStructure(String lyCode, String token) {
         synchronized (ParkPreCounter.LOCK) {
-            List<JSONObject> allPark = dmsService.getAllPark(authService.getToken());
+            List<JSONObject> allPark = dmsService.getAllPark(authService.getTokenOfServiceAccount());
             LocalDate now = LocalDate.now();
             ParkPreCounter parkPreCounter = new ParkPreCounter(allPark, new ArrayList<>(), now);
             parkPreCounter.runWithOutLease();
-            dmsService.updateToDms(parkPreCounter.getParkNeedUpload(), authService.getToken(), DMSColumn.INDUSTRIAL_PARK);
-            dmsService.updateToDms(parkPreCounter.getLeaseNeedUpload(), authService.getToken(), DMSColumn.LEASE_DETAIL);
+            dmsService.updateToDms(parkPreCounter.getParkNeedUpload(), authService.getTokenOfServiceAccount(), DMSColumn.INDUSTRIAL_PARK);
+            dmsService.updateToDms(parkPreCounter.getLeaseNeedUpload(), authService.getTokenOfServiceAccount(), DMSColumn.LEASE_DETAIL);
         }
     }
 }

+ 1 - 1
src/main/java/com/skyversation/xjcy/service/SerialNumberGenerator.java

@@ -64,7 +64,7 @@ public class SerialNumberGenerator {
     public void autoInit() {
         String token;
         try {
-            token = authService.getToken();
+            token = authService.getTokenOfServiceAccount();
         } catch (Exception e) {
             throw new RuntimeException(e);
         }

+ 4 - 1
src/main/resources/application.yml

@@ -28,4 +28,7 @@ app:
     password: ${DMS_PASSWORD:Hj@123456}
     path: ${OAUTH_LOGIN_PATH:http://121.43.55.7:10086/oauth}
   ows:
-    path: ${OWS_PATH:http://121.43.55.7:8889/geoserver/xjxm/ows}
+    path: ${OWS_PATH:http://121.43.55.7:8889/geoserver/xjxm/ows}
+  wechat:
+    appid: ${XJCY_WECHAT_APPID:wx125843453562c86c}
+    secret-key: ${XJCY_WECHAT_SECRET:6028cc345cfdbc76224d750a13519762}