微信前端的开发
.wxml文件(界面的编写)
<text>智能聊天助手</text>
<view class="top"></view>
<scroll-view class="context" scroll-y="true" scroll-top="{{scrolltop}}">
<block wx:for="{{arr}}">
<view wx:if="{{item.tag==true}}" class="right_view">
<image class="right_head" src="{{item.head}}"></image>
<view class="right">{{item.text}}</view>
</view>
<view wx:else class="left_view">
<image class="left_head" src="{{item.head}}"></image>
<view class="left">{{item.text}}</view>
</view>
</block>
</scroll-view>
<!-- adjust-position="true" 键盘弹起时,是否自动上推页面 -->
<view class="input">
<image class="yuyin" src="{{yuyin_img}}" bindtap="yuyin"></image>
<input type="text" bindinput="bindinput" class="input_text" adjust-position="true"></input>
<view bindtap="adddata" class="send_button">发送</view>
</view>
说明:
scroll-view中语句的含义介绍
- 这里定义一个滑块,作为显示聊天内容的界面
- scroll-y 属性表示纵向滑动
- 添加scroll-top属性是为了实现有信息出现在界面时它能自动弹起的效果,* * 取值与js的data中的scrolltop有关
- 采用 wx:for 循环,调用 arr 数组中的值,并使用 wx:if 条件语句来判断元素中 tag 的真假值,真为右即为发送值,假为左为接收值
- _head后缀class表示头像
- right、left名(class)表示消息内容
scroll-view外 - 下面定义为输入框的定义
- yuyin标签表示做下方的语音标识,同时使用 bindtap 绑定了js文件中的 yuyin 函数 ,使用src属性取值 data中 yuyin_img 的标识路径
- input_text采用ipnut标签,属性设置上type为 text 表示输入为文本类型,同时使用 bindinput 属性与js 文件中的 bindinput 函数连接 输入框信息获
- adjust-position=“true” 是使得在使用手机输入时 能够使手机键盘自动弹起
- send_button的view标签即是发送按钮的功能,与 js 中 adddata 函数连接
前端.js文件(数据的存储与后端的AI调用)
【注意:智能闲聊、语音识别的信息发送方式有所不同,智能闲聊发送给后端的字符串信息较短,而语音在前端转成base64格式后会变成一个非常长的字符串,已经超过了GET的请求范围,所以需要使用POST的方式请求】
【注意:使用POST 请求时,后端会反应网络安全的问题,所以需要在后端的config文件中做出语句的修改,详见后端的 config 部分】
// pages/chat_index/chat_index.js
Page({
/**
* 页面的初始数据
*/
/*data中的数据说明:
arr数组中:tag为bool类型 显示时用于判断是接收方(false),还是发送方(true)
*/
data: {
arr:[
{tag:true,text:"你好",head:"../../inco/1.jpg"},
{tag:false,text:"您好",head:"../../inco/2.jpg"},
{tag:true,text:"你叫啥",head:"../../inco/1.jpg"},
{tag:false,text:"我叫小豪",head:"../../inco/2.jpg"},
{tag:true,text:"背一首诗",head:"../../inco/1.jpg"},
{tag:false,text:"寒雨连江夜入吴,平明送客楚山孤。洛阳亲友如相问,一片冰心在玉壶。",head:"../../inco/2.jpg"},
],
inputvalue:'',//用于同步保存输入框中的值,以发送给后端
//inputfeel:'',//
scrolltop:1000,//用于聊天界面在有新消息时自动弹起的参数,与scroll-top属性相连
flag:false,//用于语音按键的开关判断true为开,false为关
yuyin_img:"../../inco/yuyin.png",//用于语音按钮标识的显示
yuyin_text:"",//用于保存后端AI返回的识别内容
yuyin_speack:""//用于调试打印录制的音频信息
},
//语音识别功能
yuyin:function(){
//1.在data中设置flag为false
//2.按钮点击时执行下面的函数
//每次点击改变flag的值 为了做一个开关按钮的功能
this.data.flag = !this.data.flag
console.log(this.data.flag)
if (this.data.flag){
this.setData({yuyin_img:"../../inco/yuyin2.png"})//更新语音按钮的标识,以表示正在录音
console.log("开始录音")
let myrecord=wx.getRecorderManager()//获取录音的工具
this.setData({ myrecord: myrecord})//把录音工具保存到data中
myrecord.start({ format:"mp3"})//开始录音
//监听录音工具停止录音的事件:当录音停止了就会调用函数
myrecord.onStop(
(res)=>{
console.log(res)//获取了录音文件的信息
this.setData({yuyin_speack:res.tempFilePath})//将语音的地址字符串给yuyin_speack
console.log(this.data.yuyin_speack)//打印录制的音频信息
/*将语音的文件转化为based4格式,因为AI语音识别的要求*/
let data1=wx.getFileSystemManager().readFileSync(res.tempFilePath,'base64')
console.log(data1)
//给后端发录音文件,因为发送的base64字符串过长,超出了 GET 请求(默认)的限度,所以使用 POST 请求方式
wx.request({
method:"POST",
url: 'http://192.168.0.101:7001/speech',
data:{
info:data1
},
success:(res)=>{
console.log(res);
this.setData({yuyin_text:res.data.data.data.text})//获取返回的语音数据
/*将返回的识别内容加入arr数组中,以显示在聊天界面*/
this.data.arr.push(
{
tag:false,
text:res.data.data.data.text,
head:"../../inco/2.jpg"
})
this.setData({arr:this.data.arr})
this.setData({scrolltop:1000})
}
})
})
}
else{
console.log("结束录音 可以获取录音文件了")
this.data.myrecord.stop()//停止录音
this.setData({yuyin_img:"../../inco/yuyin.png"})//更换录音标识为关闭
this.data.arr.push(
{
tag:true,
text:"语音已发送",
head:"../../inco/1.jpg"
})
this.setData({arr:this.data.arr})
this.setData({scrolltop:1000})
}
},
// 发送按钮的点击事件(智能闲聊功能)
adddata:function(){
var new_data=this.data.arr;
new_data.push(
{
tag:true,
text:this.data.inputvalue,
head:"../../inco/1.jpg"
}
)
this.setData({arr:new_data})
this.setData({scrolltop:1000})
//发送数据(下面data中的内容)给后端,并用success获取后端返回的数据
wx.request({
url: 'http://192.168.0.101:7001/text2',
data:{
info:this.data.inputvalue
},
success:(res)=>{
console.log(res.data.data.data.answer);
this.data.arr.push(
{
tag:false,
text:res.data.data.data.answer,
head:"../../inco/2.jpg"
})
this.setData({arr:this.data.arr})
this.setData({scrolltop:1000})
}
})
},
// 与界面输入框绑定实时获取输入框内容
bindinput:function(e){
this.setData({inputvalue:e.detail.value})
console.log(e.detail.value)
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
nodejs后端的开发
app——router.js 的代码
'use strict';
/**
* @param {Egg.Application} app - egg application
*/
module.exports = app => {
const { router, controller } = app;
//router.get('/', controller.home.index);
//该功能是设置网址为/zcx,对应调用controler文件夹中home文件中的zcx函数
router.get('/zcx', controller.home.zcx);
router.get('/zcx_2', controller.home.zcx_2);
router.get('/text', controller.home.text);
router.get('/text2', controller.home.text2);//智能闲聊接收方
router.post('/speech', controller.home.speech);//语音识别接收方
};
最后两个分别是智能闲聊接收方、语音识别接收方
app——controller——home.js
【注意:】
在计算完成签名后,即准备工作完成,即可发送给腾讯AI进行处理,需要注意的是语音识别的AI的协议要求使用 POST 请求。 method:“POST”
'use strict';
const md5=require("md5");
const fs = require('fs');
const Controller = require('egg').Controller;
class HomeController extends Controller {
// async index() {
// const { ctx } = this;
// ctx.body = '66666666';
// }
//router.js中使用代码调用zxc函数的功能即是将ctx.body的内容传给访问的前端
async zcx() {
const { ctx } = this;
ctx.body = 'this is my data';
}
async zcx_2() {
const { ctx } = this;
ctx.body = [{"name":"derek","age":18,"grads":"10"},{"name":"derek1","age":19,"grads":"9"},{"name":"derek3","age":20,"grads":"8"}];
}
//ctx.request.query 表示后端收到的前端的数据
async text() {
const { ctx } = this;
console.log(ctx.request.query);
ctx.body = '后端收到数据';
}
//智能闲聊的AI
async text2() {
const { ctx } = this;
console.log(ctx.request.query);
//1.把前端的发过来的数据发送过AI
var url="https://api.ai.qq.com/fcgi-bin/nlp/nlp_textchat";
var obj = {
app_id: 2154766358,//申请应用时分配的
session: 'zcx',
question: ctx.request.query.info,//用户会话信息
time_stamp: parseInt((new Date().getTime() / 1000)),
nonce_str: parseInt((new Date().getTime() / 1000)),
sign: ''
};
//以下为签名信息(sign)的计算过程
//1
const newkey = Object.keys(obj).sort();
var params = {};
for (var i = 0; i < newkey.length; i++) {
params[newkey[i]] = obj[newkey[i]];
}
//2
let str = '';
for (const k in params) {
if (params.hasOwnProperty(k) && params[k]) {
str += k + '=' + encodeURIComponent(params[k]) + '&';
}
}
//3
str += 'app_key=' + 'o4ZCz3Mmzy5dTeeZ';
//4
//要去下载: npm i md5 --save 还要引入:const md5=require("md5")
var singstr=md5(str).toUpperCase();
//5,设置签名
obj.sign=singstr;
//准备工作完成,即可发送给腾讯AI进行处理,默认为GET请求
var result= await this.ctx.curl(url,{
dataType:"json",
data:obj,
})
console.log(result)
ctx.body = result;
}
//智能语音识别
async speech() {
const { ctx } = this;
console.log(ctx.request.body);
//1.把前端的发过来的数据发送过AI
var url="https://api.ai.qq.com/fcgi-bin/aai/aai_asr";
var obj = {
app_id: 2154847969,//申请应用时分配的
format:2,//语音压缩编码设置为amr
rate:8000,
speech:ctx.request.body.info,
time_stamp: parseInt((new Date().getTime() / 1000)),
nonce_str: parseInt((new Date().getTime() / 1000)),
sign: ''
};
//以下为签名信息(sign)的计算过程
//1
const newkey = Object.keys(obj).sort();
var params = {};
for (var i = 0; i < newkey.length; i++) {
params[newkey[i]] = obj[newkey[i]];
}
//2
let str = '';
for (const k in params) {
if (params.hasOwnProperty(k) && params[k]) {
str += k + '=' + encodeURIComponent(params[k]) + '&';
}
}
//3
str += 'app_key=' + 'nM2IfdlYGrrR1mgg';
//4
//要去下载: npm i md5 --save 还要引入:const md5=require("md5")
var singstr=md5(str).toUpperCase();
//5,设置签名
obj.sign=singstr;
//准备工作完成,即可发送给腾讯AI进行处理,需要注意的是语音AI的协议要求使用 POST 请求。
var result= await this.ctx.curl(url,{
dataType:"json",
data:obj,
method:"POST"
})
console.log(result)
ctx.body = result;
}
}
module.exports = HomeController;
config——config.default.js
这里做配置是为了 解决 使用POST请求时出现的网络安全问题
主要添加的语句是:
config.security = {
csrf: {
enable: false,
ignoreJSON: true,
}
}
和
return {
...config,
...userConfig,
bodyParser: {
jsonLimit: '100mb',//修改接受数量限制
formLimit: '100mb',
},
/* eslint valid-jsdoc: "off" */
'use strict';
/**
* @param {Egg.EggAppInfo} appInfo app info
*/
module.exports = appInfo => {
/**
* built-in config
* @type {Egg.EggAppConfig}
**/
const config = exports = {};
config.security = {
csrf: {
enable: false,
ignoreJSON: true,
}
}
// use for cookie sign key, should change to your own and keep security
config.keys = appInfo.name + '_1594950148528_599';
// add your middleware config here
config.middleware = [];
// add your user config here
const userConfig = {
// myAppName: 'egg',
};
return {
...config,
...userConfig,
bodyParser: {
jsonLimit: '100mb',
formLimit: '100mb',
},
};
};
这里附带后端服务器的node整个文件:
后端服务器代码