说明:我写这篇文章的主要目的是因为我在做这块的时候遇到过一些坑,也是希望后来者能少走一些弯路。



1. 科大讯飞接口服务类:
<?php
namespace service;
/**
* 科大讯飞AIUI服务
* Class AIUIService
* @package service
*/
class AIUIService
{
const APP_ID = '*****'; //讯飞AIUI开放平台注册申请应用的应用ID(appid)
const API_KEY = '****'; //接口密钥,由讯飞AIUI开放平台提供,调用方管理
const TOKEN = '*****'; //后处理token
const AES_KEY = '*****'; //加密AES KEY
/**
* @title 构造函数
* @param string $key 密钥
* @param string $method 加密方式
* @param string $iv iv向量
* @param mixed $options 还不是很清楚
*/
public function __construct()
{
$this->token = self::TOKEN;
// key是必须要设置的
$this->secret_key = self::AES_KEY;
$this->method = "AES-128-CBC";
$this->iv = self::AES_KEY;
$this->options = OPENSSL_RAW_DATA;
}
/**
* @title 签名验证
* @param $token token
* @param $timestamp 时间戳
* @param $rand 随机数
* @param $aesKey $aesKey
* @param $sign 客户端请求接口sign参数值
* @return INT
*/
public function checkAuth($sign,$timestamp,$rand,$key='')
{
//按规则拼接为字符串
$str = self::createSignature($this->token,$timestamp,$rand,$key);
///校验签名字符串:0为一致、-1为不一致
if ($str !== $sign) {
return -1;
}
return 0;
}
/**
* @title 生成签名
* @param $token
* @param $timestamp
* @param $rand
* @param string $aesKey
* @return string
*/
private static function createSignature($token,$timestamp,$rand,$key='')
{
//组装要排序的数组
$arr = [$timestamp,$token,$rand];
//字典序排序
sort($arr);
//拼接为一个字符串
$str = implode('',$arr);
//sha1加密
return sha1($str);
}
/**
* @title 加密
* @param $plaintext string 要加密的字符串
* @return string
*/
public function encrypt($plaintext){
//加密采用AES的CBC加密方式,秘钥为16字节(128bit),初始化向量复用秘钥,填充方式为PKCS7Padding。
//返回的消息要以同样的方式进行加密。
//加密过程:padding->CBC加密->base64编码
//$option 以下标记的按位或: OPENSSL_RAW_DATA 原生数据,对应数字1,不进行 base64 编码。OPENSSL_ZERO_PADDING 数据进行 base64 编码再返回,对应数字0。
return openssl_encrypt($plaintext, $this->method, $this->secret_key,$this->options, $this->iv);
}
/**
* @title 解密
* @param $ciphertext string 要解密的字符串
* @return string
*/
public function decrypt($ciphertext){
//解密过程:base64解码->CBC解密->unpadding
return openssl_decrypt($ciphertext, $this->method, $this->secret_key, $this->options, $this->iv);
}
}
2. 控制器处理:
接口调用示例:



注: 其他具体接入过程可参见文档:https://aiui.xfyun.cn/docs/access_docs
<?php
namespace app\api\controller;
use service\AIUIService;
use service\SimilarityMatch;
use think\Db;
/**
* @title 科大讯飞自定义语义库
* @class AiUi
* @auth 邹柯
* @date 2018-11-19
*/
class AiUi
{
/**
* @title 科大讯飞自定义语义库
* @return json
*/
public function accessVerification()
{
//接收参数
$param = request()->param(false);
//判断接口访问方式:POST/GET
$res = isset($_SERVER['REQUEST_METHOD']) && !strcasecmp($_SERVER['REQUEST_METHOD'],'POST');
if($res) { //POST
//是否加密:encrypttype=aes-加密、raw-不加密
$encrypttype = $param[ 'encrypttype'];
//解密
if($encrypttype == "aes"){
$DeMsgContent = (new AIUIService())->decrypt(file_get_contents("php://input"));
$DeMsgContent = json_decode($DeMsgContent,true);
}else{
//消息内容
$MsgContent = $param['Msg']['Content'];
$DeMsgContent = json_decode(base64_decode($MsgContent),true);
}
//这个地方是个坑,为空时必须返回'{"intent":{}}' 不能是'{"intent":[]}' 否则会报错,切记
if(empty($DeMsgContent["intent"])){
return '{"intent":{}}';
}
//组装要返回的问题答案
//应答码(response code),0-操作成功、4-文本没有匹配的技能场景,技能不理解或不能处理该文本
if(!empty($DeMsgContent["intent"]) && $DeMsgContent["intent"]["rc"] == 4){
$msg = $this->getMsg($DeMsgContent["intent"]["text"]);
if(empty($msg)){
$msg_text = $DeMsgContent["intent"]["text"];
$keywords = ["返回"];
if(!in_array($msg_text,$keywords)){
$msg_text = "这个技能我还没学会呢";
$DeMsgContent["intent"]["service"] = "openQA";
$DeMsgContent["intent"]["operation"] = "ANSWER";
//入库未学会的问题
$this->insertUndefineQuestion($DeMsgContent["intent"]['text']);
$DeMsgContent["intent"]["semantic"] = [
'slots'=>[
'name'=>$DeMsgContent["intent"]["text"],
'operation'=>'ANSWER'
],
];
}else{
$DeMsgContent["intent"]["service"] = "app";
$DeMsgContent["intent"]["operation"] = "EXIT";
$DeMsgContent["intent"]["semantic"] = [
'slots'=>[
'name'=>$msg_text,
'operation'=>'EXIT'
],
];
}
}else{