最近有个业务需求是制作一个H5,把用户提交的退费申请发送到企业微信审核中心,让企业内部直接审核,大致看了下开发文档,也比较简单。感叹腾讯企业微信功能太强大了,就是一个企业OA
第一步 创建应用
这里就是应用的secret,复制保存。后期接口调用需要。
我的企业里页面拉到最下方,有个企业ID,这个后期接口要用也复制保存。
应用管理里面,点审批,新建一个审核,建好审核点进去URL最后一个参数就是 模板ID 复制保存,后期要用。至此所需主要开发接口参数已经完成,下面开始我们的接口开发。
第二步 接口开发
一 获取access_token
获取access_token是调用企业微信API接口的第一步,相当于创建了一个登录凭证,其它的业务API接口,都需要依赖于access_token来鉴权调用者身份。
因此开发者,在使用业务接口前,要明确access_token的颁发来源,使用正确的access_token。
请求方式: GET(HTTPS)
请求地址: https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET
注:此处标注大写的单词ID和SECRET,为需要替换的变量,根据实际获取值更新。其它接口也采用相同的标注,不再说明。
参数说明:
参数 | 必须 | 说明 |
---|---|---|
corpid | 是 | 企业ID,获取方式参考:术语说明-corpid |
corpsecret | 是 | 应用的凭证密钥,获取方式参考:术语说明-secret |
权限说明:
每个应用有独立的secret,获取到的access_token只能本应用使用,所以每个应用的access_token应该分开来获取
返回结果:
{
"errcode": 0,
"errmsg": "ok",
"access_token": "accesstoken000001",
"expires_in": 7200
}
参数说明:
参数 | 说明 |
---|---|
errcode | 出错返回码,为0表示成功,非0表示调用失败 |
errmsg | 返回码提示语 |
access_token | 获取到的凭证,最长为512字节 |
expires_in | 凭证的有效时间(秒) |
注意事项:
开发者需要缓存access_token,用于后续接口的调用(注意:不能频繁调用gettoken接口,否则会受到频率拦截)。当access_token失效或过期时,需要重新获取。
access_token的有效期通过返回的expires_in来传达,正常情况下为7200秒(2小时),有效期内重复获取返回相同结果,过期后获取会返回新的access_token。
由于企业微信每个应用的access_token是彼此独立的,所以进行缓存时需要区分应用来进行存储。
access_token至少保留512字节的存储空间。
企业微信可能会出于运营需要,提前使access_token失效,开发者应实现access_token失效时重新获取的逻辑。
access_token.php 缓存access_token
<?php exit();?>{"access_token":"29_uoNmjFXbZDVZczklqDaQWl5jh3WuuRnluOEbCpTjkcUjrF1YF9v57kZtfPO8DFtwEK4YUYaeDnOIPJwXozDMHTMm43gF1zXZ4Wn86HnSZf9Ox2xH9_cXB3GqnpwIowF6NK_MPH0c3fyjjzkIBHKhADAWCF","expire_time":1577934448}
获取access_token类
主要的功能就是调用接口获取access_token,第一次结果写入到access_token.php文件里面,以后从这个文件缓存里获取,如果缓存时间过了7000秒,那么就重新获取,避免接口调用上限制。
<?php
class WXSDK {
private $appId;
private $appSecret;
public function __construct($appId, $appSecret) {
$this->appId = $appId;//这里就是企业微信ID
$this->appSecret = $appSecret;//这里就是你应用的secret
$this->filePath = __DIR__.'/';
}
private function getAccessToken() {
// access_token 应该全局存储与更新,以下代码以写入到文件中做示例
$data = json_decode($this->get_php_file($this->filePath."access_token.php"));
if ($data->expire_time < time()) {
// 如果是企业号用以下URL获取access_token
// $url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$this->appId&corpsecret=$this->appSecret";
$url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$this->appId&corpsecret=$this->appSecret";
$res = json_decode($this->httpGet($url),TRUE);
$access_token = $res['access_token'];
if ($access_token) {
$data->expire_time = time() + 7000;
$data->access_token = $access_token;
$this->set_php_file($this->filePath."access_token.php", json_encode($data));
}
} else {
$access_token = $data->access_token;
}
return $access_token;
}
private function httpGet($url) {
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_TIMEOUT, 500);
// 为保证第三方服务器与微信服务器之间数据传输的安全性,所有微信接口采用https方式调用,必须使用下面2行代码打开ssl安全校验。
// 如果在部署过程中代码在此处验证失败,请到 http://curl.haxx.se/ca/cacert.pem 下载新的证书判别文件。
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curl, CURLOPT_URL, $url);
$res = curl_exec($curl);
curl_close($curl);
return $res;
}
private function get_php_file($filename) {
return trim(substr(file_get_contents($filename), 15));
}
private function set_php_file($filename, $content) {
$fp = fopen($filename, "w");
fwrite($fp, "<?php exit();?>" . $content);
fclose($fp);
}
}
//调用类
$wxsdk =new WXSDK($appid, $appsecret);
$wxconfig = $wxsdk->getAccessToken();
二 发送企业审核
企业可通过审批应用或自建应用Secret调用本接口,代应用可见范围内员工在企业微信“审批应用”内提交指定类型的审批申请。
请求方式:POST(HTTPS)
请求地址: https://qyapi.weixin.qq.com/cgi-bin/oa/applyevent?access_token=ACCESS_TOKEN
请求示例:
{
"creator_userid": "WangXiaoMing",
"template_id": "3Tka1eD6v6JfzhDMqPd3aMkFdxqtJMc2ZRioeFXkaaa",
"use_template_approver":0,
"choose_department":2,
"approver": [
{
"attr": 2,
"userid": ["WuJunJie","WangXiaoMing"]
},
{
"attr": 1,
"userid": ["LiuXiaoGang"]
}
],
"notifyer":[ "WuJunJie","WangXiaoMing" ],
"notify_type" : 1,
"apply_data": {
"contents": [
{
"control": "Text",
"id": "Text-15111111111",
"value": {
"text": "文本填写的内容"
}
}
]
},
"summary_list": [
{
"summary_info": [{
"text": "摘要第1行",
"lang": "zh_CN"
}]
},
{
"summary_info": [{
"text": "摘要第2行",
"lang": "zh_CN"
}]
},
{
"summary_info": [{
"text": "摘要第3行",
"lang": "zh_CN"
}]
}
]
}
参数说明:
参数 | 必须 | 说明 |
---|---|---|
access_token | 是 | 调用接口凭证。必须使用审批应用或企业内自建应用的secret获取,获取方式参考:文档-获取access_token |
creator_userid | 是 | 申请人userid,此审批申请将以此员工身份提交,申请人需在应用可见范围内 |
template_id | 是 | 模板id。可在“获取审批申请详情”、“审批状态变化回调通知”中获得,也可在审批模板的模板编辑页面链接中获得。暂不支持通过接口提交[打卡补卡][调班]模板审批单。 |
use_template_approver | 是 | 审批人模式:0-通过接口指定审批人、抄送人(此时approver、notifyer等参数可用); 1-使用此模板在管理后台设置的审批流程,支持条件审批。默认为0 |
choose_department | 否 | 提单者提单部门id,不填默认为主部门 |
approver | 是 | 审批流程信息,用于指定审批申请的审批流程,支持单人审批、多人会签、多人或签,可能有多个审批节点,仅use_template_approver为0时生效。 |
└ userid | 是 | 审批节点审批人userid列表,若为多人会签、多人或签,需填写每个人的userid |
└ attr | 是 | 节点审批方式:1-或签;2-会签,仅在节点为多人审批时有效 |
notifyer | 否 | 抄送人节点userid列表,仅use_template_approver为0时生效。 |
notify_type | 否 | 抄送方式:1-提单时抄送(默认值); 2-单据通过后抄送;3-提单和单据通过后抄送。仅use_template_approver为0时生效。 |
apply_data | 是 | 审批申请数据,可定义审批申请中各个控件的值,其中必填项必须有值,选填项可为空,数据结构同“获取审批申请详情”接口返回值中同名参数“apply_data” |
└ contents | 是 | 审批申请详情,由多个表单控件及其内容组成,其中包含需要对控件赋值的信息 |
└ └ control | 是 | 控件类型:Text-文本;Textarea-多行文本;Number-数字;Money-金额;Date-日期/日期+时间;Selector-单选/多选;;Contact-成员/部门;Tips-说明文字;File-附件;Table-明细;Location-位置;RelatedApproval-关联审批单;Formula-公式;DateRange-时长; |
└ └ id | 是 | 控件id:控件的唯一id,可通过“获取审批模板详情”接口获取 |
└ └ value | 是 | 控件值 ,需在此为申请人在各个控件中填写内容不同控件有不同的赋值参数,具体说明详见附录。模板配置的控件属性为必填时,对应value值需要有值。 |
summary_list | 是 | 摘要信息,用于显示在审批通知卡片、审批列表的摘要信息,最多3行 |
└ summary_info | 是 | 摘要行信息,用于定义某一行摘要显示的内容 |
└ └ text | 是 | 摘要行显示文字,用于记录列表和消息通知的显示,不要超过20个字符 |
└ └ lang | 是 | 摘要行显示语言 |
接口频率限制 600次/分钟
当模板的控件为必填属性时,表单中对应的控件必须有值。
返回结果:
{
"errcode": 0,
"errmsg": "ok",
"sp_no": "202001010001"
}
下面是服务器端代码:
//这里是H5表单提交过来的数据 要和你审核模板里面建的字段对应,对变量做下过滤验证,我这里就不写太详细,只是管理功能演示
$phone = isset($_POST['phone']) ? test_input($_POST['phone']) : 0;
$uname = test_input($_POST['uname']);
$school = test_input($_POST['school']);
$fee = test_input($_POST['fee']);
$reason = isset($_POST['reason']) ? test_input($_POST['reason']) : "-";
$course = isset($_POST['course']) ? test_input($_POST['course']) : "-";
$arrdate = array(
"creator_userid"=>"这里是企业内部人员ID",
"template_id"=>"这里是你审核的模板ID",
"use_template_approver"=>0,
"approver"=>array(
0=>array(
"attr"=>2,
"userid"=>array(0=>"审核人员ID")
),
1=>array(
"attr"=>1,
"userid"=>array(0=>"审核人员ID")
)
),
"notifyer"=>array(0=>"抄送人员ID"),
"notify_type"=>1,
"apply_data"=>array(
"contents"=>array(
0=>array(
"control"=>"Text",
"id"=>"Text-1613713873031",
"value"=>array("text"=>$uname)
),
1=>array(
"control"=>"Text",
"id"=>"Text-1613713907687",
"value"=>array("text"=>$phone)
),
2=>array(
"control"=>"Text",
"id"=>"Text-1586930029610",
"value"=>array("text"=>$course)
),
3=>array(
"control"=>"Money",
"id"=>"Money-1586930048663",
"value"=>array("new_money"=>$fee)
),
4=>array(
"control"=>"Text",
"id"=>"Textarea-1586930035590",
"value"=>array("text"=>$reason)
),
5=>array(
"control"=>"Selector",
"id"=>"Selector-1613725714279",
"value"=>array("selector"=>array(
"type"=>"single",
"options"=>array(
0=>array("key"=>$school)
)
))
)
)
)
);
httpPost($url,json_encode($arrdate));
$data['code'] = 'true';
$data['msg'] = '申请成功!';
$data['phone'] = $phone;
echo json_encode($data);
function test_input($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
function httpPost($url,$params=false,$ispost=0){
$httpInfo = array();
$ch = curl_init();
curl_setopt( $ch, CURLOPT_HTTP_VERSION , CURL_HTTP_VERSION_1_1 );
curl_setopt( $ch, CURLOPT_USERAGENT , 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22' );
curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT , 30 );
curl_setopt( $ch, CURLOPT_TIMEOUT , 30);
curl_setopt( $ch, CURLOPT_RETURNTRANSFER , true );
if( $ispost )
{
curl_setopt( $ch , CURLOPT_POST , true );
curl_setopt( $ch , CURLOPT_POSTFIELDS , $params );
curl_setopt( $ch , CURLOPT_URL , $url );
}
else
{
if($params){
curl_setopt( $ch , CURLOPT_URL , $url.'?'.$params );
}else{
curl_setopt( $ch , CURLOPT_URL , $url);
}
}
$response = curl_exec( $ch );
if ($response === FALSE) {
//echo "cURL Error: " . curl_error($ch);
return false;
}
$httpCode = curl_getinfo( $ch , CURLINFO_HTTP_CODE );
$httpInfo = array_merge( $httpInfo , curl_getinfo( $ch ) );
curl_close( $ch );
return $response;
}
前端H5代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<title>退费申请</title>
<link rel="stylesheet" type="text/css" href="js/jquery.dialogbox.css"/>
<link rel="stylesheet" type="text/css" href="js/style.css"/>
</head>
<body>
<div class="formtop">
<h2>退费申请</h2>
<div class="bmcon">
<div class="formk">
<input type="text" class="forminput" name="uname" id="uname" placeholder="输入报名人姓名" />
</div>
<div class="formk">
<input type="text" class="forminput" name="uphone" id="uphone" placeholder="输入报名人手机号" />
</div>
<div class="formk">
<select class="forminput" name="school" id="school">
<option value="">请选择报名校区</option>
<option value="option-1613725808454">人广</option>
<option value="option-1613725808456">金桥</option>
<option value="option-1613725808457">上南</option>
</select>
</div>
<div class="formk">
<input type="text" class="forminput" name="course" id="course" placeholder="报名课程" />
</div>
<div class="formk">
<input type="text" class="forminput" name="fee" id="fee" placeholder="退费金额" />
</div>
<div class="formk">
<input type="text" class="forminput" name="reason" id="reason" placeholder="退费原因" />
</div>
<div class="formk">
<button type="submit" class="btncheng" id="fmbtn" onClick="return CheckAll();">立即申请</button>
</div>
</div>
<div id="btn-dialogBox"></div>
</div>
<script src="js/jquery.js" type="text/javascript" charset="utf-8"></script>
<script src="js/jquery.dialogBox.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
function mess(info) {
$('#btn-dialogBox').dialogBox({
width: 300,
height: 200,
autoHide: true,
time: 1500,
effect: 'flip-horizontal',
//title: 'title text',
content: '<img src="js/ifau.png"><br/><br/>' + info
});
}
function mess2(info) {
$('#btn-dialogBox').dialogBox({
width: 300,
height: 200,
autoHide: true,
time: 1500,
effect: 'flip-horizontal',
//title: 'title text',
content: '<img src="js/isuc.png"><br/><br/>' + info
});
}
function CheckAll() {
if($("#uname").val() == "") {
mess("用户名不能为空!");
$("#uname").focus();
return false;
}
if(!/^[\u4e00-\u9fa5]{2,5}$/.test($("#uname").val())) {
mess("姓名必须是中文");
$("#uname").focus();
return false;
}
if(!/^1[3|4|5|7|8]\d{9}$/.test($("#uphone").val())) {
mess("请填写正确的手机号码");
$("#uphone").focus();
return false;
}
if($("#school option:selected").attr("value") == "") {
mess("请选择交费校区!");
$("#school").focus();
return false;
}
if(!/(^[1-9]\d*$)/.test($("#fee").val())) {
mess("请填写退费金额");
$("#fee").focus();
return false;
}
//AJAX数据提交
$.ajax({
type:"POST",
url:"/weixin/api/examples/ApprovalTest.php",
data:{
uname:$("#uname").val(),
phone:$("#uphone").val(),
school:$("#school").val(),
course:$("#course").val(),
fee:$("#fee").val(),
reason:$("#reason").val()
},
cache:false,
dataType:'json',
beforeSend:function(){$("#fmbtn").html("数据提交中,请稍后...");},
success:function(data) {
if(data.code =="true" ){
mess(data.msg);
$("input").val("");
$("#fmbtn").html("立即提交");
}
},
error : function() {
mess(data.msg);
}
});
}
</script>
</script>
</body>
</html>
至此,功能完成。后台审核的机制可以企业微信里面自己设置比较简单可以让企业微信管理员按需求设置。