微信小程序实现图林机器人聊天和百度AI语音识别的简单人工客服

用微信小程序实现简单的人工客服

最近在做软件工程的课程设计,选择性的做了微信小程序的简单的人工客服。在这里对该课程设计的原理和实现进行一个讲解,也算做一个总结和笔记,方便自己以后查看、复习和帮助大家的学习。


  • 工具:微信开发者工具、Eclipse、Tomcat、图林机器人V2版、百度AI语音识别
  • 实现原理与过程
    (1)微信小程序获取用户输入的文字、图片或语音消息,发送给Tomcat服务器;
    (2)Tomcat服务器接收到消息后交给对应的servel进行处理。因为微信小程序发送文字消息可以使用GET方式,发送文件必须使用POST方式,所以只用一个Sevlet就可以进行处理。
    (3)Servlet获取GET方式的请求,则调用doGet方法,获取请求携带的用户消息,发送给图林机器人,得到图林机器人的随机回复消息后,返回给微信小程序,显示给用户;
    (4)Servlet获取POST方式的请求,则调用doPost方法,获取微信小程序上传的文件消息,先保存在本地的临时文件夹中,在判断是图片消息还是语音消息。如果是图片消息,仅保存在本地,不做任何处理,只返回给微信小程序一个文字回复,如:“图片我看不懂,你自己看”;如果是语音消息,则发送给百度AI进行语音识别,识别成功,在将结果发送给图林机器人,取得图林机器人的回复后,返回给微信小程序,显示给用户;识别失败,则返回给微信小程序一个文字回复,如:“太吵了,听不清楚。。”;

  • 实现过程

(1)服务器实现:
- 项目结构:
chat
- 导入jar包到lib文件夹下:
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。导入时需要填入自己的测试码,不然不可以使用。切记哦!!


以上就是整个小项目的实现过程,难点在于图林机器人的跨域问题,还有语音格式的转换,需要注意一下。好啦,以上就是我对这个小项目的一个小的总结,希望可以帮助到你们。

发布了46 篇原创文章 · 获赞 11 · 访问量 3917

猜你喜欢

转载自blog.csdn.net/qq_42197800/article/details/91186258