业务背景:公司目前的审批是走erp后台,然后还得走纸质的打印出来审批,影响效率,现在需要在企业微信直接提交申请单,然后审批,最后将审批数据同步到erp后台。
第一步:验证回调Url,在配置回调url时,企业微信后台需要验证,因此需要写一个回调验证接口
/**
* 回调接口接受企业微信验证
* @param msgSignature
* @param timestamp
* @param nonce
* @param echostr
* @param response
*/
@GetMapping("/receiveData")
public void verifyURL(@RequestParam("msg_signature") String msgSignature,
@RequestParam("timestamp") Integer timestamp,
@RequestParam("nonce") String nonce,
@RequestParam("echostr") String echostr,HttpServletResponse response){
PrintWriter out=null;
try {
out = response.getWriter();
if(StringUtils.isNotEmpty(echostr)){
log.info("验证回调url有效性, 签名:{},时间戳:{},随机数:{},密文:{}", msgSignature,timestamp,nonce,echostr);
String sEchoStr=workWeiXinService.verifyURL(msgSignature,timestamp,nonce,echostr);
if(StringUtils.isNotEmpty(sEchoStr)){
out.write(sEchoStr);
out.flush();
}
}
} catch (IOException e) {
log.error("企业微信回调url验证错误",e.getMessage());
e.printStackTrace();
}finally {
out.close();
}
}
@Override
public String verifyURL(String msgSignature, Integer timestamp, String nonce, String echostr) {
String sEchoStr=null;
try {
WXBizJsonMsgCrypt wxBizJsonMsgCrypt = new WXBizJsonMsgCrypt(token, encodingAesKey, corpId);
sEchoStr = wxBizJsonMsgCrypt.VerifyURL(msgSignature, timestamp.toString(),
nonce, echostr);
log.info("verifyurl echostr:{}",sEchoStr);
} catch (Exception e) {
log.error("验证URL失败,错误原因请查看异常,{}",e.getMessage());
//验证URL失败,错误原因请查看异常
e.printStackTrace();
}
return sEchoStr;
}
第二步:给前端生成签名
(签名一共分两种,笔者刚开始只给自建应用生成了签名,后来在和前端联调过程中,发现报错,验签不通过,后经官方文档排查还需要给jsAPI生成签名)
@Override
public SignatureResponse getSignature(String url) {
String approvalTicket =getApprovalTicket();
JSONObject approvalObject=getSignature(approvalTicket,url);
String jsapiTicket = getJsapiTicket();
JSONObject jsapiObject=getSignature(jsapiTicket,url);
SignatureResponse response=new SignatureResponse();
response.setSignature(approvalObject.get("signature").toString());
response.setNonce(approvalObject.get("nonceStr").toString());
response.setTimestamp(Long.valueOf(approvalObject.get("timestamp").toString()));
response.setCorpId(corpId);
response.setSignatureConfig(jsapiObject.get("signature").toString());
response.setNonceConfig(jsapiObject.get("nonceStr").toString());
response.setTimestampConfig(Long.valueOf(jsapiObject.get("timestamp").toString()));
return response;
}
private JSONObject getSignature(String ticket, String url) {
JSONObject jsonObject=new JSONObject();
String nonce=getNonce();
Long timeStamp=getTimeStamp();
String plainText= "jsapi_ticket=" + ticket +
"&noncestr=" + nonce +
"×tamp=" + timeStamp +
"&url=" + url;
String signature=null;
try {
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(plainText.getBytes("UTF-8"));
signature = byteToHex(crypt.digest());
jsonObject.put("nonceStr",nonce);
jsonObject.put("timestamp",timeStamp);
jsonObject.put("signature",signature);
jsonObject.put("ticket",ticket);
}catch (NoSuchAlgorithmException e){
e.printStackTrace();
}catch (UnsupportedEncodingException e){
e.printStackTrace();
}
return jsonObject;
}
private String getJsapiTicket() {
String accessToken = getAccessToken();
String jsapiTicket = getJsapiTicket(accessToken);
return jsapiTicket;
}
/**
* 获取审批应用的ticket
* @return
*/
private String getApprovalTicket() {
String accessToken = getAccessToken();
String approvalTicket=getApprovalTicket(accessToken);
return approvalTicket;
}
private String getAccessToken() {
String reqAccessTokenUrl="https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid="+corpId+"&corpsecret="+corpSecret;
String reqAccessTokenResponse = HttpClient.sendGetRequest(reqAccessTokenUrl, Constants.ENCODE_UTF8);
String accessToken=null;
if(StringUtils.isNotEmpty(reqAccessTokenResponse)){
AccessTokenResponse accessTokenResponse = com.alibaba.fastjson.JSONObject.parseObject(reqAccessTokenResponse, AccessTokenResponse.class);
accessToken=accessTokenResponse.getAccess_token();
}
return accessToken;
}
private String getAccessToken() {
String reqAccessTokenUrl="https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid="+corpId+"&corpsecret="+corpSecret;
String reqAccessTokenResponse = HttpClient.sendGetRequest(reqAccessTokenUrl, Constants.ENCODE_UTF8);
String accessToken=null;
if(StringUtils.isNotEmpty(reqAccessTokenResponse)){
AccessTokenResponse accessTokenResponse = com.alibaba.fastjson.JSONObject.parseObject(reqAccessTokenResponse, AccessTokenResponse.class);
accessToken=accessTokenResponse.getAccess_token();
}
return accessToken;
}
private String getJsapiTicket(String accessToken) {
String reqJsapiTicketUrl="https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token="+accessToken;
String reqJsapiTicketResponse = HttpClient.sendGetRequest(reqJsapiTicketUrl, Constants.ENCODE_UTF8);
String jsapiTicket=null;
if(StringUtils.isNotEmpty(reqJsapiTicketResponse)){
JsapiTicketResponse response= JSONObject.parseObject(reqJsapiTicketResponse,JsapiTicketResponse.class);
jsapiTicket=response.getTicket();
}
log.info("accessToken:{},jsapiTicket:{}",accessToken,jsapiTicket);
return jsapiTicket;
}
private String getApprovalTicket(String accessToken) {
String reqJsapiTicketUrl="https://qyapi.weixin.qq.com/cgi-bin/ticket/get?access_token="+accessToken+"&type=agent_config";
String reqJsapiTicketResponse = HttpClient.sendGetRequest(reqJsapiTicketUrl, Constants.ENCODE_UTF8);
String jsapiTicket=null;
if(StringUtils.isNotEmpty(reqJsapiTicketResponse)){
JsapiTicketResponse response= JSONObject.parseObject(reqJsapiTicketResponse,JsapiTicketResponse.class);
jsapiTicket=response.getTicket();
}
log.info("accessToken:{},approvalTicket:{}",accessToken,jsapiTicket);
return jsapiTicket;
}
private String byteToHex(byte[] hash) {
Formatter formatter = new Formatter();
for (byte b : hash)
{
formatter.format("%02x", b);
}
String result = formatter.toString();
formatter.close();
return result;
}
private Long getTimeStamp() {
return System.currentTimeMillis() / 1000;
}
private String getNonce() {
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 16; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
第三步:回调接口接受企业微信审批数据
/**
* 回调接口接受企业微信审核数据
* @param requestDto
* @return
*/
@PostMapping("/receiveData")
public void receiveData(@RequestParam("msg_signature") String msgSignature,
@RequestParam("timestamp") Integer timestamp,
@RequestParam("nonce") String nonce,
@RequestBody(required=false) WorkWeiXinCallBack requestDto,HttpServletResponse response) {
log.info("回调接口接受企业微信审核数据信息, 信息:{}", JSONObject.toJSONString(requestDto));
workWeiXinService.receiveData(msgSignature,timestamp,nonce,requestDto);
PrintWriter out=null;
try {
out = response.getWriter();
out.write("SUCCESS");
out.flush();
}catch (IOException e) {
log.error("企业微信回调url验证错误",e.getMessage());
e.printStackTrace();
}finally {
out.close();
}
}
@Override
public void receiveData(String msgSignature,Integer timestamp,String nonce,WorkWeiXinCallBack requestDto) {
try {
WXBizJsonMsgCrypt wxBizJsonMsgCrypt = new WXBizJsonMsgCrypt(token, encodingAesKey, corpId);
String sReqData = com.alibaba.fastjson.JSONObject.toJSONString(requestDto);
String sMsg = wxBizJsonMsgCrypt.DecryptMsg(msgSignature, timestamp.toString(), nonce, sReqData);
log.info("after decrypt msg:{} ", sMsg);
JSONObject json = new JSONObject(sMsg);
String Content = json.getString("Content");
log.info("回调接口接受企业微信审核数据信息Content:{}",Content);
WorkWeiXinCallBackAuditInfo workWeiXinCallBackAuditInfo = com.alibaba.fastjson.JSONObject.parseObject(Content, WorkWeiXinCallBackAuditInfo.class);
//WorkWeiXinCallBackAuditInfo workWeiXinCallBackAuditInfo =(WorkWeiXinCallBackAuditInfo) JSON.parse(Content);
//申请单当前审批状态:1-审批中;2-已通过;3-已驳回;4-已撤销
if(workWeiXinCallBackAuditInfo.getApprovalInfo().getOpenSpStatus()!=2){
return;
}
String thirdNo = workWeiXinCallBackAuditInfo.getApprovalInfo().getThirdNo();
log.info("回调接口接受企业微信审核数据,审核订单号:{}",thirdNo);
//获取访问token
String accessToken=getAccessToken();
String reqApprovalInfoUrl="https://qyapi.weixin.qq.com/cgi-bin/oa/getapprovaldetail?access_token="+accessToken;
Map<String,Object> param=new HashMap<>();
param.put("sp_no",thirdNo);
Map<String,Object> result = HttpClient.sendPostRequestForJson(reqApprovalInfoUrl, param,Map.class);
if(result ==null || result.get("info") ==null){
return;
}
//-----------------------企业微信审核申请数据解析start-------------------------
String applyUserId = workWeiXinCallBackAuditInfo.getApprovalInfo().getApplyUserId();
String applyUserName = workWeiXinCallBackAuditInfo.getApprovalInfo().getApplyUserName();
Map<String,Object> info = (Map<String, Object>) result.get("info");
Map<String,Object> applyData = (Map<String, Object>) info.get("apply_data");
List<Map<String,Object>> contentList= (List<Map<String, Object>>) applyData.get("contents");
String overseasServiceRate=null;
String startTime=null;
String endTime=null;
String overseasServiceRemark=null;
Integer memberId=0;
for(Map<String,Object> content:contentList){
Map<String,Object> value= (Map<String, Object>) content.get("value");
if("startTime".equals(content.get("id").toString())){
startTime=value.get("text").toString();
}else if("endTime".equals(content.get("id").toString())){
endTime=value.get("text").toString();
}else if("overseasServiceRemark".equals(content.get("id").toString())){
overseasServiceRemark=value.get("text").toString();
}else if("overseasServiceRate".equals(content.get("id").toString())){
overseasServiceRate=value.get("text").toString();
}else if("memberId".equals(content.get("id").toString())){
memberId= Ints.tryParse(value.get("text").toString());
}
}
//-------------------------企业微信审核申请数据解析end-----------------------
AppMemOverSeasService appMemOverSeasService=new AppMemOverSeasService();
appMemOverSeasService.setOverseasServiceRate(new BigDecimal(overseasServiceRate));
appMemOverSeasService.setOverseasServiceState((byte)1);
appMemOverSeasService.setEffectiveStatus((byte)1);
appMemOverSeasService.setCreateTime(new Date());
appMemOverSeasService.setStartTime(DateUtil.StrToDate(startTime,"yyyy-MM-dd HH:mm"));
appMemOverSeasService.setEndTime(DateUtil.StrToDate(endTime,"yyyy-MM-dd HH:mm"));
appMemOverSeasService.setMemberId(memberId);
appMemOverSeasService.setOverseasServiceRemark(overseasServiceRemark);
appMemOverSeasServiceMapper.insertSelective(appMemOverSeasService);
log.info("回调接口接受企业微信审核数据信息新增成功:{}", com.alibaba.fastjson.JSONObject.toJSONString(appMemOverSeasService));
try {
String content= "会员境外服务费修改成功\r\n" +
"会员id:" +applyUserId+"\r\n"+
"会员名称:" +applyUserName+"\r\n"+
"境外服务费:" +overseasServiceRate+"\r\n"+
"开始时间:" +startTime+"\r\n"+
"结束时间:"+endTime+"\r\n";
new Thread(() -> weixinService.sendQYWXMessageByNum(content, applyUserId)).start();
} catch (Exception e) {
log.error("发送企业微信通知异常:{}",e.getMessage());
e.printStackTrace();
}
log.info("推送通知提交人,企业微信通知成功,审核单编号:{}", thirdNo);
} catch (Exception e) {
log.info("解密失败,失败原因请查看异常:",e.getMessage());
// 解密失败,失败原因请查看异常
e.printStackTrace();
}
}
企业微信审批后 会自动回调企业服务器地址