业务背景:公司目前的审批是走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();
    }
}


企业微信审批后 会自动回调企业服务器地址