最近接到公司校招面试结果查询的一个需求,功能点其实很简单:那就是能让HR导入excel数据,然后学生可以通过微信公众账号查询到自己的面试结果。

后端技术选型用了PHP+Mysql,前端用Angular+Bootstrap,所有功能自己一个人来完成,算是重新复习了一遍Mysql的操作,以及PHP的一些语法,顺便把Angular真正用于项目中,总体收获蛮大。

微信公众号开发这一块算是卡了我比较久的,毕竟官方文档太久没更新,而且官方文档真心写得不咋地,然后就直接拿了公司一个同事的PHP代码过来改,顺利完成功能。

公众号开发配置

  1. 要进行公众号的服务开发,首先你得有自己的一个服务器用于接收公众号转发过来的信息进行处理返回结果,国内免费的如sinaapp或者duapp都可以用于接收。
  2. 既然要通过公众号来转发服务,那就得有一个公众号,可以到微信公众平台注册一个个人账号。
  3. 公众号注册成功之后,进入到开发者中心,也就是登录进去之后左边导航最下面的一个,发现有个服务器配置,然后填写一下服务器配置信息,说明如下:
    • URL(服务器地址):这个可以填写你需要接收公众号转发信息的地址,当然这个地址需要配置一下能够让微信认识你的东西,稍后说。
    • Token(令牌):一个md5戳,可以随便填,建议以你自己服务器地址域名的md5戳,这样子方便记忆。
    • EncodingAESKey(消息加解密密钥):这个随机生成一个即可。
    • 消息加解密方式:这个明文就行了。
  4. 上面的配置写好之后,不要那么快保存,因为你一点击保存微信就会请求你填写的URL看能否返回它需要的信息,所以我们要先配置一下自己的服务器,下面代码是用PHP写的:

//下面的TOKEN写自己在公众号填写的那个
define("TOKEN", "XXXXXXXXXXXXXXXXXX");
define("YTAG", "0.1220200001300000");

//验证来源
function checkSignature(){
$signature = $_GET["signature"]; //微信加密签名
$timestamp = $_GET["timestamp"]; //时间戳
$nonce = $_GET["nonce"]; //随机数
$token = TOKEN; //自定义的token参数
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr); //三个参数进行字典序排序
$tmpStr = implode( $tmpArr ); //将三个参数字符串拼接成一个字符串
$tmpStr = sha1( $tmpStr ); //进行SHA1加密,获得最终加密后的字符串

if( $tmpStr == $signature ){ //加密字符串与微信加密签名对比
return true;
}else{
return false;
}
}

if( checkSignture() ){
// 接口校验时返回
echo $_GET['echostr'];
}

上面服务器代码配置好了之后,就可以在微信公众号保存服务器配置信息,提示成功就绑定成功了。当然即使你的配置完全正确,它还是有时会提醒你失败,重复保存几次就好了。

正式进入开发

微信公众号绑定你的服务器成功之后就可以正式进入开发了,当然在正式开发过程中模块化的思维还是首选的,我们可以考虑主要分几个模块:

  1. 接收从公众号转发过来的关键词,判断进行何种处理,姑且为config.php
  2. 主入口文件,这里包括了一开始进行验证的代码,然后验证成功之后进行处理的主函数,就为index.php吧。
  3. 函数集合,将所有需要处理的函数都写在这里面,那就叫function.php吧。

下面给出这三个文件的一个非常简单的demo,实现你在公众号发送hello world的时候回复你hello world

  • index.php (主入口文件)

define("TOKEN", "xxxxxxxxxxxxxx");
define("YTAG", "0.1220200001300000");

//验证来源
function checkSignature(){
$signature = $_GET["signature"]; //微信加密签名
$timestamp = $_GET["timestamp"]; //时间戳
$nonce = $_GET["nonce"]; //随机数
$token = TOKEN; //自定义的token参数
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr); //三个参数进行字典序排序
$tmpStr = implode( $tmpArr ); //将三个参数字符串拼接成一个字符串
$tmpStr = sha1( $tmpStr ); //进行SHA1加密,获得最终加密后的字符串

if( $tmpStr == $signature ){ //加密字符串与微信加密签名对比
return true;
}else{
return false;
}
}

//回复消息
function responseMsg(){
$beginTime = microtime(true);
//载入配置文件及函数
include_once("function.php") ;
include_once("config.php") ;
//获取微信传参
$postStr = $GLOBALS["HTTP_RAW_POST_DATA"];

if (!empty($postStr)){
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
$postInfo['FromUserName'] = $postObj->FromUserName;
$postInfo['ToUserName'] = $postObj->ToUserName;
$postInfo['CreateTime'] = $postObj->CreateTime;
$postInfo['MsgType'] = $postObj->MsgType;
$postInfo['MsgId'] = $postObj->MsgId;
$postInfo['Content'] = trim($postObj->Content);
$postInfo['Event'] = trim($postObj->Event);
$postInfo['EventKey'] = trim($postObj->EventKey);
$postInfo['PicUrl'] = trim($postObj->PicUrl);
$postInfo['Recognition'] = trim($postObj->Recognition);
$postInfo['Ytag'] = "?YTAG=".YTAG;
}
$msgType = $postInfo['MsgType'];
$keyword = $postInfo['Content'];
$event = $postInfo['Event'];
$eventKey = $postInfo['EventKey'];

switch ($msgType) {
// 下面只处理了text的情况,实际还可以包括:voice、event、image等
case 'text':
{
if(isset($keyword)){
//config.php中存在时,执行特定操作
if (function_exists($config[$keyword]['fName'])) {
$postInfo['Parameter'] = $config[$keyword]['fPara'];
$output = $config[$keyword]['fName']($postInfo);
}
//不存在时
else{
$postInfo['Parameter'] = $config["default"]['fPara'];
$output = $config["default"]['fName']($postInfo);
}
}
}
break;
}
$endTime = microtime(true);
}

if(checkSignature()){
responseMsg();
}

  • config.php(关键词匹配)

//下面的fName表示执行的函数名称,fPara表示回复的信息
$config = array(
// 当传入hello world的时候执行showTxt函数,传入hello world参数
"hello world"=>array(
"fName"=>"showTxt",
"fPara"=>"hello world"
),
"default"=>array(
"fName"=>"showTxt",
"fPara"=>"还在开发中……"
),
);

  • function.php(具体执行函数)
 
function showTxt($postInfo) {
$fromUsername = $postInfo ['FromUserName'];
$toUsername = $postInfo ['ToUserName'];
$txtContent = $postInfo ['Parameter'];

$time = time ();
$msgType = "text";
// 下面的xml格式是微信要求返回的,必须严格按照这个格式返回,不能私自添加属性
$textTpl = "<xml>
<ToUserName><![CDATA[$fromUsername]]></ToUserName>
<FromUserName><![CDATA[$toUsername]]></FromUserName>
<CreateTime>$time</CreateTime>
<MsgType><![CDATA[$msgType]]></MsgType>
<Content><![CDATA[$txtContent]]></Content>
<FuncFlag>0</FuncFlag>
</xml>";
return $textTpl;
}

测试

上面代码写好丢到服务器,就可以进行测试了,当然了,更常规的测试应该是直接用微信测试号来进行测试,我懒得搞了,就可以丢服务器测试了,这个时候发送hello world给公众号,然后就可以看到回复了hello world,说明我们的开发就成功了。

至于我的需求,那就是PHP的逻辑代码问题了,有了上面的骨架,需要啥功能基本上可以完成了。

注意点

  1. 在发送消息给公众号若回复:该公众号暂时无法提供服务。这个是微信的机制导致的,我们开发过程经常采用被动回复机制,在5秒内微信若收不到回复就会提示这种信息,所以我们可以在还没做好回复之前先回复空的消息,微信会自动不理。若还有问题就要检查一下我们自己的代码,比如回复的XML格式问题,必须严格按照微信的要求,另外检查一下代码是否有echo、var_dump等代码干扰。