用微信小程序实现简单的人工客服
最近在做软件工程的课程设计,选择性的做了微信小程序的简单的人工客服。在这里对该课程设计的原理和实现进行一个讲解,也算做一个总结和笔记,方便自己以后查看、复习和帮助大家的学习。
- 工具:微信开发者工具、Eclipse、Tomcat、图林机器人V2版、百度AI语音识别
- 实现原理与过程:
(1)微信小程序获取用户输入的文字、图片或语音消息,发送给Tomcat服务器;
(2)Tomcat服务器接收到消息后交给对应的servel进行处理。因为微信小程序发送文字消息可以使用GET方式,发送文件必须使用POST方式,所以只用一个Sevlet就可以进行处理。
(3)Servlet获取GET方式的请求,则调用doGet方法,获取请求携带的用户消息,发送给图林机器人,得到图林机器人的随机回复消息后,返回给微信小程序,显示给用户;
(4)Servlet获取POST方式的请求,则调用doPost方法,获取微信小程序上传的文件消息,先保存在本地的临时文件夹中,在判断是图片消息还是语音消息。如果是图片消息,仅保存在本地,不做任何处理,只返回给微信小程序一个文字回复,如:“图片我看不懂,你自己看”;如果是语音消息,则发送给百度AI进行语音识别,识别成功,在将结果发送给图林机器人,取得图林机器人的回复后,返回给微信小程序,显示给用户;识别失败,则返回给微信小程序一个文字回复,如:“太吵了,听不清楚。。”;
- 实现过程:
(1)服务器实现:
- 项目结构:
- 导入jar包到lib文件夹下:
- 新建web项目,创建一个Servelt(图中的MyServlet):
servlet部分
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
@WebServlet("/MyServlet")
public class MyServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
private static final String URL = "http://openapi.tuling123.com/openapi/api/v2";
private Tuling tuling = new Tuling();
//进行文本聊天
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
String reqMes = request.getParameter("reqMes").toString().trim();
// System.out.println(reqMes);
//将用户发送过来的消息转换成JSON格式
String reqJson = tuling.getReqJson(reqMes);
//发送消息到图林机器人,获取图林机器人的回复
String sendPost = tuling.sendPost(URL, reqJson);
//获取图林机器人回复的JSON消息中的主要回复内容
String string = tuling.getResultMeg("[" + sendPost +"]");
System.out.println("get提问: "+reqMes);
System.out.println("回复: "+string);
//返回值给小程序
//将图林回复的消息string返回给小程序
PrintWriter out = response.getWriter();
out.write(string);
out.flush();
out.close();
// response.sendRedirect("index.jsp");
}
//文件上传
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("上传文件");
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
response.setCharacterEncoding("utf-8");
//调用下面的保存文件的方法
uploadFile(request, response);
// 在uploadFile方法中实现了上述的 实现过程与原理 中的第四步
//语音 小程序API录音后Silk格式转码PCM
}
//获取微信小程序发送的文件消息,并进行处理
private void uploadFile(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
//得到服务器的真是磁盘路径
String realPath = this.getServletContext().getRealPath("/");
File file = new File(realPath+"upload");
System.out.println(realPath);
//要返回给微信小程序的结果
String string = "";
//磁盘文件工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(file);//设置临时文件存放的位置
factory.setSizeThreshold(1024*1024*10);//上传文件的大小5M
//servlet文件上传对象
ServletFileUpload upload = new ServletFileUpload(factory);
try {
//将普通的request请求转成FileItem的列表集合
List<FileItem> list = upload.parseRequest(request);
FileItem uploadFile = null;
//增强型for循环
for(FileItem item : list) {
if(item.isFormField()) {//是普通表单元素
}else {//否则说明是文件上传
uploadFile = item;
}
}
//获取文件后缀
String name = uploadFile.getName();
String suffix = name.substring(name.lastIndexOf("."));
//做真正的上传,将临时文件 存储到 指定磁盘位置
//图片或语音要保存的绝对路径
String skil = "";
if(".silk".equals(suffix)) {
skil = "该语音文件要保存的绝对路径"+suffix;
}else {
// 图片文件要保存的绝对路径
skil = (file+"\\"+UUID.randomUUID()+"."+suffix);
}
System.out.println(skil);
// 按保存的文路径创建文件
File saveFile = new File(skil);
//数据写入保存的文件
try {
uploadFile.write(saveFile);
} catch (Exception e) {
e.printStackTrace();
}
// 分别对图片或语音消息进行处理
if(".silk".equals(suffix)) {
//处理语音消息
System.out.println("这是一个语音");
Sample sample = new Sample();
string = sample.checkMeg();
System.out.println(string);
}else {
System.out.println("这是一个图片");
string = "图片自己看,我看不懂";
}
} catch (FileUploadException e) {
e.printStackTrace();
} finally {
// 对语音或图片进行不同的回复处理,返回给微信小程序
if(!"太吵了,听不清楚..".equals(string) && !"图片自己看,我看不懂".equals(string)) {
System.out.println("将调用图林机器人");
System.out.println("get提问: "+string);
String reqJson = tuling.getReqJson(string);
String sendPost = tuling.sendPost(URL, reqJson);
string = tuling.getResultMeg("[" + sendPost +"]");
System.out.println("回复: "+string);
}
//返回值给小程序
PrintWriter out = response.getWriter();
out.write(string);
out.flush();
out.close();
}
}
}
图灵机器人部分:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
public class Tuling {
private static final String apikey ="图林机器人apikey";
private static final String uid = "用户id";
/**
* 将输入的信息转换为json对象
* @param reqMes
* @return
*/
public String getReqJson(String reqMes) {
//请求json
JSONObject reqJson = new JSONObject();
//输入类型
int reqType = 0;
//输入信息json,如文本,图片
JSONObject perception = new JSONObject();
//输入的信息
JSONObject inputText = new JSONObject();
inputText.put("text", reqMes);
perception.put("inputText",inputText);
//用户信息
JSONObject userInfo = new JSONObject();
userInfo.put("apiKey", apikey);
userInfo.put("userId", uid);
reqJson.put("reqType", reqType);
reqJson.put("perception", perception);
reqJson.put("userInfo", userInfo);
return reqJson.toString();
}
/**
* 发送请求
* @param url
* @param reqJson
* @return
*/
public String sendPost(String url, String reqJson) {
String status = "";
String responseStr = "";
PrintWriter out = null;
BufferedReader in = null;
try {
URL realUrl = new URL(url);
//打开url连接
URLConnection urlCon = realUrl.openConnection();
HttpURLConnection httpUrlConnection = (HttpURLConnection) urlCon;
//设置请求属性
httpUrlConnection.setRequestProperty("Content-Type", "application/json");
httpUrlConnection.setRequestProperty("x-adviewrtb-version", "2.1");
//发送post请求必须设置一下两行
httpUrlConnection.setDoOutput(true);
httpUrlConnection.setDoInput(true);
//获取URLConnection对象对应的输出流
out = new PrintWriter(httpUrlConnection.getOutputStream());
//发送请求参数
out.write(reqJson);
//flush输出流的缓冲
out.flush();
httpUrlConnection.connect();
//定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(new InputStreamReader(httpUrlConnection.getInputStream()));
String line = "";
while((line = in.readLine())!=null) {
responseStr += line;
}
status = new Integer(httpUrlConnection.getResponseCode()).toString();
httpUrlConnection.disconnect();
} catch (IOException e) {
System.out.println("发送post请求出现异常");
e.printStackTrace();
} finally {
try {
if(out != null) {
out.close();
}
if(in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return responseStr;
}
/**
* JSONObject将字符串转换为json,取到消息信息
* @param tulingStr
* @return
*/
public String getResultMeg(String tulingStr) {
JSONObject object = null;
String strResult = "";
JSONArray jsonArray = new JSONArray(tulingStr);
for(int i = 0; i < jsonArray.length(); i++) {
//System.out.println("jsonArray: " + jsonArray.getJSONObject(i));
object = jsonArray.getJSONObject(i);
strResult = object.getString("results");
//System.out.println("results: "+string);
}
JSONArray jsonArray1 = new JSONArray(strResult);
for(int i = 0; i < jsonArray1.length(); i++) {
//System.out.println("jsonArray1: " + jsonArray1.getJSONObject(i));
object = jsonArray1.getJSONObject(i);
strResult = object.getString("values");
//System.out.println("values: "+string);
}
JSONArray jsonArray2 = new JSONArray("["+strResult+"]");
for(int i = 0; i < jsonArray2.length(); i++) {
//System.out.println("jsonArray2: " + jsonArray2.getJSONObject(i));
object = jsonArray2.getJSONObject(i);
strResult = object.getString("text");
//System.out.println("text: "+string);
}
return strResult;
}
}
百度AI语音识别部分:
DeCoder类,用来进行语音格式的转换详细信息,查看此文章
import java.io.IOException;
import org.nutz.lang.Encoding;
import org.nutz.lang.Lang;
public class DeCoder {
/**
* 解码为pcm格式
* @param silk
* @param pcm
* @return
*/
public static boolean getPcm(String silk,String pcm){
boolean flag = true;
String cmd="cmd.exe /c C:\\silk_v3_decoder.exe "+silk+" "+pcm+" -quiet";
System.out.println("转码到pcm...");
try
{
StringBuilder msg = Lang.execOutput(cmd, Encoding.CHARSET_GBK);
System.out.println(msg);
}
catch (IOException e)
{
e.printStackTrace();
flag = false;
}
return flag;
}
/**
* 转码为MP3格式
* @param pcm
* @param mp3
* @return
*/
public static boolean getMp3(String pcm,String mp3){
boolean flag = true;
String command = "cmd.exe /c C:\\ffmpeg.exe -y -f s16le -ar 24000 -ac 1 -i "+pcm+" "+mp3+" ";
System.out.println("转码到mp3...");
try {
StringBuilder sb = Lang.execOutput(command, Encoding.CHARSET_GBK);
System.out.println(sb);
} catch (IOException e) {
e.printStackTrace();
flag = false;
}
return flag;
}
}
Sample类,发送消息给百度AI,进行语音识别详细信息,查看此官方文档
import org.json.JSONObject;
import com.baidu.aip.speech.AipSpeech;
import net.sf.json.JSONArray;
import net.sf.json.JSONException;
public class Sample {
//设置APPID/AK/SK 都填写自己的
public static final String APP_ID = "app_id";
public static final String API_KEY = "app_key";
public static final String SECRET_KEY = "secret_key";
//进行语音格式转换和发送给百度AI进行语音识别
public static String checkMeg() {
//此路径详见DeCoder类
String silk = "D:\\test.silk";
String pcm = "D:\\test.pcm";
String mp3 = "D:\\test.mp3";
DeCoder deCoder = new DeCoder();
boolean b = deCoder.getPcm(silk, pcm);
System.out.println(b);
if(b)
deCoder.getMp3(pcm, mp3);
String filePath = "D:\\test.pcm";
String sendPost = sendPost(filePath);
//post返回剪切好的字符串
return sendPost;
}
//发送给百度AI进行语音识别,详见官方文档
private static String sendPost(String filePath) {
// 初始化一个AipSpeech
AipSpeech client = new AipSpeech(APP_ID, API_KEY, SECRET_KEY);
// 可选:设置网络连接参数
client.setConnectionTimeoutInMillis(2000);
client.setSocketTimeoutInMillis(60000);
// 可选:设置代理服务器地址, http和socket二选一,或者均不设置
// client.setHttpProxy("proxy_host", proxy_port); // 设置http代理
// client.setSocketProxy("proxy_host", proxy_port); // 设置socket代理
// 可选:设置log4j日志输出格式,若不设置,则使用默认配置
// 也可以直接通过jvm启动参数设置此环境变量
// System.setProperty("aip.log4j.conf", "path/to/your/log4j.properties");
// 调用接口
JSONObject res = client.asr(filePath, "pcm", 16000, null);
String string = res.toString(2);
System.out.println("返回JSON: "+string);
//获取结果
String resultMeg = getResultMeg("["+string+"]");
// System.out.println(resultMeg);
//剪切字符串
int last = resultMeg.indexOf("\"]");
String lastResult = resultMeg.substring(2, last);
// System.out.println(lastResult);
//将剪切好的字符串返回
return lastResult;
}
//对百度AI返回的结果进行解析,获取回复字段消息
public static String getResultMeg(String tulingStr) {
net.sf.json.JSONObject object = null;
String strResult = "";
JSONArray jsonArray = new JSONArray(tulingStr);
for(int i = 0; i < jsonArray.length(); i++) {
object = (net.sf.json.JSONObject) jsonArray.getJSONObject(i);
try {
strResult = object.getString("result");
} catch (JSONException e) {
// e.printStackTrace();
strResult = "[\""+"太吵了,听不清楚.."+"\"]";
}
}
return strResult;
}
}
(2)微信小程序实现:
这部分我是直接使用的github的项目,只是做了功能部分的修改,直接下载导入微信开发者工具,修改servlet访问路径即可。下载地址:微信小程序实现,提取码:gnzo。导入时需要填入自己的测试码,不然不可以使用。切记哦!!
以上就是整个小项目的实现过程,难点在于图林机器人的跨域问题,还有语音格式的转换,需要注意一下。好啦,以上就是我对这个小项目的一个小的总结,希望可以帮助到你们。