用微信小程序实现简单的人工客服
最近在做软件工程的课程设计,选择性的做了微信小程序的简单的人工客服。在这里对该课程设计的原理和实现进行一个讲解,也算做一个总结和笔记,方便自己以后查看、复习和帮助大家的学习。
- 工具:微信开发者工具、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) {//请求jsonJSONObject 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) {// 初始化一个AipSpeechAipSpeech 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。导入时需要填入自己的测试码,不然不可以使用。切记哦!!
以上就是整个小项目的实现过程,难点在于图林机器人的跨域问题,还有语音格式的转换,需要注意一下。好啦,以上就是我对这个小项目的一个小的总结,希望可以帮助到你们。