|
|
@@ -1,6 +1,8 @@
|
|
|
package com.fs.app.controller;
|
|
|
|
|
|
import com.fs.app.service.QwDataCallbackService;
|
|
|
+import com.fs.common.config.RedisTenantContext;
|
|
|
+import com.fs.framework.datasource.TenantDataSourceManager;
|
|
|
import com.fs.qw.domain.QwCompany;
|
|
|
import com.fs.qw.mapper.QwCompanyMapper;
|
|
|
import com.fs.qwApi.util.AesException;
|
|
|
@@ -12,6 +14,7 @@ import org.springframework.web.bind.annotation.*;
|
|
|
import org.w3c.dom.Document;
|
|
|
import org.xml.sax.InputSource;
|
|
|
|
|
|
+import javax.annotation.Resource;
|
|
|
import javax.xml.parsers.DocumentBuilder;
|
|
|
import javax.xml.parsers.DocumentBuilderFactory;
|
|
|
import java.io.StringReader;
|
|
|
@@ -20,19 +23,19 @@ import java.io.StringReader;
|
|
|
public class QwController {
|
|
|
org.slf4j.Logger logger= LoggerFactory.getLogger(getClass());
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
@Autowired
|
|
|
private QwCompanyMapper qwCompanyMapper;
|
|
|
|
|
|
@Autowired
|
|
|
private QwDataCallbackService qwDataCallbackService;
|
|
|
|
|
|
+ @Resource
|
|
|
+ private TenantDataSourceManager tenantDataSourceManager;
|
|
|
+
|
|
|
/**
|
|
|
* 功能描述:
|
|
|
* 应用详情 --> 回调配置中 --> 数据回调URL
|
|
|
+ * SaaS 多租户支持:根据 corpId 查询租户ID,切换数据源
|
|
|
*/
|
|
|
@GetMapping("/qw/data/{corpId}")
|
|
|
public String data(@PathVariable String corpId,
|
|
|
@@ -40,8 +43,28 @@ public class QwController {
|
|
|
@RequestParam("timestamp") String timestamp,
|
|
|
@RequestParam("nonce") String nonce,
|
|
|
@RequestParam("echostr") String echostr) {
|
|
|
-// logger.info("数据回调URL-微信调用dataGet请求");
|
|
|
- return getVerify(msg_signature, timestamp, nonce, echostr,corpId);
|
|
|
+ QwCompany qwCompany = qwCompanyMapper.selectQwCompanyByCorpId(corpId);
|
|
|
+ if (qwCompany == null || qwCompany.getTenantId() == null) {
|
|
|
+ logger.error("[QwCallback] 未找到企业配置或租户ID,corpId={}", corpId);
|
|
|
+ return "error";
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 切换到租户数据源
|
|
|
+ tenantDataSourceManager.ensureSwitchByTenantId(qwCompany.getTenantId());
|
|
|
+ // 设置 Redis 租户上下文
|
|
|
+ RedisTenantContext.setTenantId(qwCompany.getTenantId());
|
|
|
+
|
|
|
+ return getVerify(msg_signature, timestamp, nonce, echostr, corpId, qwCompany);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("[QwCallback] 数据回调GET请求处理失败,corpId={}, tenantId={}",
|
|
|
+ corpId, qwCompany.getTenantId(), e);
|
|
|
+ return "error";
|
|
|
+ } finally {
|
|
|
+ // 清理租户上下文
|
|
|
+ RedisTenantContext.clear();
|
|
|
+ tenantDataSourceManager.clear();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@PostMapping("/qw/data/{corpId}")
|
|
|
@@ -51,79 +74,98 @@ public class QwController {
|
|
|
@RequestParam("timestamp") String timestamp,
|
|
|
@RequestParam(value = "corpid", required = false) String corpid,
|
|
|
@RequestParam("nonce") String nonce) {
|
|
|
- return dataCallback(msg_signature,timestamp,nonce,requestBody,corpId);
|
|
|
+ QwCompany qwCompany = qwCompanyMapper.selectQwCompanyByCorpId(corpId);
|
|
|
+ if (qwCompany == null || qwCompany.getTenantId() == null) {
|
|
|
+ logger.error("[QwCallback] 未找到企业配置或租户ID,corpId={}", corpId);
|
|
|
+ return "error";
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 切换到租户数据源
|
|
|
+ tenantDataSourceManager.ensureSwitchByTenantId(qwCompany.getTenantId());
|
|
|
+ // 设置 Redis 租户上下文
|
|
|
+ RedisTenantContext.setTenantId(qwCompany.getTenantId());
|
|
|
+
|
|
|
+ return dataCallback(msg_signature, timestamp, nonce, requestBody, corpId, qwCompany);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("[QwCallback] 数据回调POST请求处理失败,corpId={}, tenantId={}",
|
|
|
+ corpId, qwCompany.getTenantId(), e);
|
|
|
+ return "error";
|
|
|
+ } finally {
|
|
|
+ // 清理租户上下文
|
|
|
+ RedisTenantContext.clear();
|
|
|
+ tenantDataSourceManager.clear();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- public String getVerify(String sVerifyMsgSig,String sVerifyTimeStamp,
|
|
|
- String sVerifyNonce,String sVerifyEchoStr,String corpId){
|
|
|
- QwCompany qwCompany= qwCompanyMapper.selectQwCompanyByCorpId(corpId);
|
|
|
+ public String getVerify(String sVerifyMsgSig, String sVerifyTimeStamp,
|
|
|
+ String sVerifyNonce, String sVerifyEchoStr, String corpId, QwCompany qwCompany) {
|
|
|
String token = qwCompany.getToken();
|
|
|
String encodingAESKey = qwCompany.getEncodingAesKey();
|
|
|
|
|
|
WXBizMsgCrypt wxcpt = null;
|
|
|
try {
|
|
|
wxcpt = new WXBizMsgCrypt(token, encodingAESKey, corpId);
|
|
|
- }catch (AesException E){
|
|
|
+ } catch (AesException E) {
|
|
|
+ logger.error("[QwCallback] 创建解密对象失败,corpId={}", corpId, E);
|
|
|
return "error";
|
|
|
}
|
|
|
String sEchoStr; //需要返回的明文
|
|
|
try {
|
|
|
sEchoStr = wxcpt.VerifyURL(sVerifyMsgSig, sVerifyTimeStamp,
|
|
|
sVerifyNonce, sVerifyEchoStr);
|
|
|
-// logger.info("企业微信验证事件:"+corpId);
|
|
|
+ logger.info("[QwCallback] 企业微信验证成功,corpId={}, tenantId={}", corpId, qwCompany.getTenantId());
|
|
|
} catch (Exception e) {
|
|
|
- logger.info("企业微信验证失败:"+corpId);
|
|
|
- //验证URL失败,错误原因请查看异常
|
|
|
+ logger.error("[QwCallback] 企业微信验证失败,corpId={}, tenantId={}", corpId, qwCompany.getTenantId(), e);
|
|
|
return "error";
|
|
|
}
|
|
|
- return sEchoStr;
|
|
|
+ return sEchoStr;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- public String dataCallback(String sVerifyMsgSig,String sVerifyTimeStamp,String sVerifyNonce,String sData,String corpId){
|
|
|
- QwCompany qwCompany= qwCompanyMapper.selectQwCompanyByCorpId(corpId);
|
|
|
+ public String dataCallback(String sVerifyMsgSig, String sVerifyTimeStamp,
|
|
|
+ String sVerifyNonce, String sData, String corpId, QwCompany qwCompany) {
|
|
|
String token = qwCompany.getToken();
|
|
|
String encodingAESKey = qwCompany.getEncodingAesKey();
|
|
|
-// logger.info("企业微信回调事件companyId:"+corpId);
|
|
|
String result = "error";
|
|
|
WXBizMsgCrypt wxcpt = null;
|
|
|
try {
|
|
|
Object[] encrypt = XMLParse.extract(sData);
|
|
|
String sSuiteid = (String) encrypt[2];
|
|
|
wxcpt = new WXBizMsgCrypt(token, encodingAESKey, sSuiteid);
|
|
|
- }catch (AesException E){
|
|
|
+ } catch (AesException E) {
|
|
|
+ logger.error("[QwCallback] 提取加密信息失败,corpId={}, tenantId={}",
|
|
|
+ corpId, qwCompany.getTenantId(), E);
|
|
|
return result;
|
|
|
}
|
|
|
- try{
|
|
|
+ try {
|
|
|
String sMsg = wxcpt.DecryptMsg(sVerifyMsgSig, sVerifyTimeStamp, sVerifyNonce, sData);
|
|
|
- logger.info("企业微信回调事件:"+sMsg);
|
|
|
- // 加密成功
|
|
|
- // TODO: 解析出明文xml标签的内容进行处理
|
|
|
- // For example:
|
|
|
+ logger.info("[QwCallback] 企业微信回调事件,corpId={}, tenantId={}, msg={}",
|
|
|
+ corpId, qwCompany.getTenantId(), sMsg);
|
|
|
+
|
|
|
+ // 解析出明文xml标签的内容进行处理
|
|
|
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
|
|
DocumentBuilder db = dbf.newDocumentBuilder();
|
|
|
StringReader sr = new StringReader(sMsg);
|
|
|
InputSource is = new InputSource(sr);
|
|
|
Document document = db.parse(is);
|
|
|
- qwDataCallbackService.dataCallback(document,corpId,qwCompany);
|
|
|
+ qwDataCallbackService.dataCallback(document, corpId, qwCompany);
|
|
|
|
|
|
- }
|
|
|
- catch(Exception e)
|
|
|
- {
|
|
|
- e.printStackTrace();
|
|
|
- // 加密失败
|
|
|
- logger.error("加密失败:"+result);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("[QwCallback] 解密回调数据失败,corpId={}, tenantId={}",
|
|
|
+ corpId, qwCompany.getTenantId(), e);
|
|
|
return result;
|
|
|
}
|
|
|
result = "success";
|
|
|
- return result;
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 功能描述:
|
|
|
* 应用详情 --> 回调配置中 --> 数据回调URL
|
|
|
+ * SaaS 多租户支持:根据 corpId 查询租户ID,切换数据源
|
|
|
*/
|
|
|
@GetMapping("/qwServer/data/{corpId}")
|
|
|
public String ServerData(@PathVariable String corpId,
|
|
|
@@ -131,8 +173,30 @@ public class QwController {
|
|
|
@RequestParam("timestamp") String timestamp,
|
|
|
@RequestParam("nonce") String nonce,
|
|
|
@RequestParam("echostr") String echostr) {
|
|
|
- logger.info("数据回调URLServer-微信调用dataGet请求");
|
|
|
- return getVerifyServer(msg_signature, timestamp, nonce, echostr,corpId);
|
|
|
+ QwCompany qwCompany = qwCompanyMapper.selectQwCompanyByCorpId(corpId);
|
|
|
+ if (qwCompany == null || qwCompany.getTenantId() == null) {
|
|
|
+ logger.error("[QwCallback] 未找到企业配置或租户ID,corpId={}", corpId);
|
|
|
+ return "error";
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 切换到租户数据源
|
|
|
+ tenantDataSourceManager.ensureSwitchByTenantId(qwCompany.getTenantId());
|
|
|
+ // 设置 Redis 租户上下文
|
|
|
+ RedisTenantContext.setTenantId(qwCompany.getTenantId());
|
|
|
+
|
|
|
+ logger.info("[QwCallback] 数据回调URLServer-微信调用dataGet请求,corpId={}, tenantId={}",
|
|
|
+ corpId, qwCompany.getTenantId());
|
|
|
+ return getVerifyServer(msg_signature, timestamp, nonce, echostr, corpId, qwCompany);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("[QwCallback] Server数据回调GET请求处理失败,corpId={}, tenantId={}",
|
|
|
+ corpId, qwCompany.getTenantId(), e);
|
|
|
+ return "error";
|
|
|
+ } finally {
|
|
|
+ // 清理租户上下文
|
|
|
+ RedisTenantContext.clear();
|
|
|
+ tenantDataSourceManager.clear();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@PostMapping ("/qwServer/data/{corpId}")
|
|
|
@@ -142,36 +206,58 @@ public class QwController {
|
|
|
@RequestParam("timestamp") String timestamp,
|
|
|
@RequestParam("nonce") String nonce,
|
|
|
@RequestParam(value = "corpid", required = false) String corpid) {
|
|
|
- logger.info("数据回调URLServer-dataPost");
|
|
|
- return qwDataCallbackService.dataCallbackServer(msg_signature,timestamp,nonce,requestBody,corpId);
|
|
|
+ QwCompany qwCompany = qwCompanyMapper.selectQwCompanyByCorpId(corpId);
|
|
|
+ if (qwCompany == null || qwCompany.getTenantId() == null) {
|
|
|
+ logger.error("[QwCallback] 未找到企业配置或租户ID,corpId={}", corpId);
|
|
|
+ return "error";
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 切换到租户数据源
|
|
|
+ tenantDataSourceManager.ensureSwitchByTenantId(qwCompany.getTenantId());
|
|
|
+ // 设置 Redis 租户上下文
|
|
|
+ RedisTenantContext.setTenantId(qwCompany.getTenantId());
|
|
|
+
|
|
|
+ logger.info("[QwCallback] 数据回调URLServer-dataPost,corpId={}, tenantId={}",
|
|
|
+ corpId, qwCompany.getTenantId());
|
|
|
+ return qwDataCallbackService.dataCallbackServer(msg_signature, timestamp, nonce, requestBody, corpId);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("[QwCallback] Server数据回调POST请求处理失败,corpId={}, tenantId={}",
|
|
|
+ corpId, qwCompany.getTenantId(), e);
|
|
|
+ return "error";
|
|
|
+ } finally {
|
|
|
+ // 清理租户上下文
|
|
|
+ RedisTenantContext.clear();
|
|
|
+ tenantDataSourceManager.clear();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public String getVerifyServer(String sVerifyMsgSig, String sVerifyTimeStamp,
|
|
|
- String sVerifyNonce, String sVerifyEchoStr, String corpId){
|
|
|
-
|
|
|
- QwCompany qwCompany= qwCompanyMapper.selectQwCompanyByCorpId(corpId);
|
|
|
+ String sVerifyNonce, String sVerifyEchoStr, String corpId, QwCompany qwCompany) {
|
|
|
String token = qwCompany.getToken();
|
|
|
String encodingAESKey = qwCompany.getEncodingAesKey();
|
|
|
-// String corpId = qwCompanyConfig.getCorpId();
|
|
|
+
|
|
|
WXBizMsgCrypt wxcpt = null;
|
|
|
try {
|
|
|
wxcpt = new WXBizMsgCrypt(token, encodingAESKey, corpId);
|
|
|
- }catch (AesException E){
|
|
|
+ } catch (AesException E) {
|
|
|
+ logger.error("[QwCallback] Server创建解密对象失败,corpId={}", corpId, E);
|
|
|
return "error";
|
|
|
}
|
|
|
String sEchoStr; //需要返回的明文
|
|
|
try {
|
|
|
sEchoStr = wxcpt.VerifyURL(sVerifyMsgSig, sVerifyTimeStamp,
|
|
|
sVerifyNonce, sVerifyEchoStr);
|
|
|
- logger.info("企业微信验证事件需要返回的明文Server:"+sEchoStr);
|
|
|
+ logger.info("[QwCallback] 企业微信Server验证成功,corpId={}, tenantId={}, echoStr={}",
|
|
|
+ corpId, qwCompany.getTenantId(), sEchoStr);
|
|
|
} catch (Exception e) {
|
|
|
- logger.error("企业微信验证失败Serve:"+corpId+":"+e);
|
|
|
- //验证URL失败,错误原因请查看异常
|
|
|
+ logger.error("[QwCallback] 企业微信Server验证失败,corpId={}, tenantId={}",
|
|
|
+ corpId, qwCompany.getTenantId(), e);
|
|
|
return "error";
|
|
|
}
|
|
|
- return sEchoStr;
|
|
|
+ return sEchoStr;
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -179,7 +265,8 @@ public class QwController {
|
|
|
|
|
|
|
|
|
/**
|
|
|
- *服务商-代开发应用-获取 SuiteTicket的事件回调
|
|
|
+ * 服务商-代开发应用-获取 SuiteTicket的事件回调
|
|
|
+ * SaaS 多租户支持:根据 corpId 查询租户ID,切换数据源
|
|
|
*/
|
|
|
@GetMapping("/qwServerAuth/data/{corpId}")
|
|
|
public String qwServerAuthDataWGet(@RequestParam("msg_signature") String msg_signature,
|
|
|
@@ -188,44 +275,87 @@ public class QwController {
|
|
|
@RequestParam("echostr") String echostr,
|
|
|
@RequestParam(value = "auth_corpid", required = false) String authCorpid,
|
|
|
@RequestParam(value = "corpid", required = false) String corpid,
|
|
|
- @PathVariable String corpId) {
|
|
|
- return getVerifyServerAuth(msg_signature, timestamp, nonce, echostr,corpId);
|
|
|
+ @PathVariable String corpId) {
|
|
|
+ QwCompany qwCompany = qwCompanyMapper.selectQwCompanyByCorpId(corpId);
|
|
|
+ if (qwCompany == null || qwCompany.getTenantId() == null) {
|
|
|
+ logger.error("[QwCallback] 未找到企业配置或租户ID,corpId={}", corpId);
|
|
|
+ return "error";
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 切换到租户数据源
|
|
|
+ tenantDataSourceManager.ensureSwitchByTenantId(qwCompany.getTenantId());
|
|
|
+ // 设置 Redis 租户上下文
|
|
|
+ RedisTenantContext.setTenantId(qwCompany.getTenantId());
|
|
|
+
|
|
|
+ return getVerifyServerAuth(msg_signature, timestamp, nonce, echostr, corpId, qwCompany);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("[QwCallback] ServerAuth数据回调GET请求处理失败,corpId={}, tenantId={}",
|
|
|
+ corpId, qwCompany.getTenantId(), e);
|
|
|
+ return "error";
|
|
|
+ } finally {
|
|
|
+ // 清理租户上下文
|
|
|
+ RedisTenantContext.clear();
|
|
|
+ tenantDataSourceManager.clear();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
public String getVerifyServerAuth(String sVerifyMsgSig, String sVerifyTimeStamp,
|
|
|
- String sVerifyNonce, String sVerifyEchoStr, String qwCorpID){
|
|
|
-
|
|
|
- QwCompany qwCompany= qwCompanyMapper.selectQwCompanyByCorpId(qwCorpID);
|
|
|
+ String sVerifyNonce, String sVerifyEchoStr, String qwCorpID, QwCompany qwCompany) {
|
|
|
String token = qwCompany.getToken();
|
|
|
String encodingAESKey = qwCompany.getEncodingAesKey();
|
|
|
|
|
|
WXBizMsgCrypt wxcpt = null;
|
|
|
try {
|
|
|
wxcpt = new WXBizMsgCrypt(token, encodingAESKey, qwCorpID);
|
|
|
- }catch (AesException E){
|
|
|
+ } catch (AesException E) {
|
|
|
+ logger.error("[QwCallback] ServerAuth创建解密对象失败,corpId={}", qwCorpID, E);
|
|
|
return "error";
|
|
|
}
|
|
|
String sEchoStr; //需要返回的明文
|
|
|
try {
|
|
|
sEchoStr = wxcpt.VerifyURL(sVerifyMsgSig, sVerifyTimeStamp,
|
|
|
sVerifyNonce, sVerifyEchoStr);
|
|
|
- logger.info("企业微信验证事件需要返回的明文ServerAuth:"+sEchoStr);
|
|
|
+ logger.info("[QwCallback] 企业微信ServerAuth验证成功,corpId={}, tenantId={}, echoStr={}",
|
|
|
+ qwCorpID, qwCompany.getTenantId(), sEchoStr);
|
|
|
} catch (Exception e) {
|
|
|
- logger.error("企业微信验证失败ServeAuth:"+qwCorpID+":"+e);
|
|
|
- //验证URL失败,错误原因请查看异常
|
|
|
+ logger.error("[QwCallback] 企业微信ServerAuth验证失败,corpId={}, tenantId={}",
|
|
|
+ qwCorpID, qwCompany.getTenantId(), e);
|
|
|
return "error";
|
|
|
}
|
|
|
- return sEchoStr;
|
|
|
+ return sEchoStr;
|
|
|
}
|
|
|
|
|
|
- @PostMapping ("/qwServerAuth/data/{corpID}")
|
|
|
+ @PostMapping("/qwServerAuth/data/{corpID}")
|
|
|
public String ServerAuthData(@PathVariable String corpID,
|
|
|
@RequestBody String requestBody,
|
|
|
@RequestParam("msg_signature") String msg_signature,
|
|
|
@RequestParam("timestamp") String timestamp,
|
|
|
@RequestParam("nonce") String nonce,
|
|
|
@RequestParam(value = "corpid", required = false) String corpid) {
|
|
|
-// logger.info("数据回调URLServerAuth-dataPost");
|
|
|
- return qwDataCallbackService.dataCallbackServerAuth(msg_signature,timestamp,nonce,requestBody,corpID);
|
|
|
+ QwCompany qwCompany = qwCompanyMapper.selectQwCompanyByCorpId(corpID);
|
|
|
+ if (qwCompany == null || qwCompany.getTenantId() == null) {
|
|
|
+ logger.error("[QwCallback] 未找到企业配置或租户ID,corpId={}", corpID);
|
|
|
+ return "error";
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 切换到租户数据源
|
|
|
+ tenantDataSourceManager.ensureSwitchByTenantId(qwCompany.getTenantId());
|
|
|
+ // 设置 Redis 租户上下文
|
|
|
+ RedisTenantContext.setTenantId(qwCompany.getTenantId());
|
|
|
+
|
|
|
+ logger.info("[QwCallback] 数据回调URLServerAuth-dataPost,corpId={}, tenantId={}",
|
|
|
+ corpID, qwCompany.getTenantId());
|
|
|
+ return qwDataCallbackService.dataCallbackServerAuth(msg_signature, timestamp, nonce, requestBody, corpID);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("[QwCallback] ServerAuth数据回调POST请求处理失败,corpId={}, tenantId={}",
|
|
|
+ corpID, qwCompany.getTenantId(), e);
|
|
|
+ return "error";
|
|
|
+ } finally {
|
|
|
+ // 清理租户上下文
|
|
|
+ RedisTenantContext.clear();
|
|
|
+ tenantDataSourceManager.clear();
|
|
|
+ }
|
|
|
}
|
|
|
}
|