├── .gitignore ├── LICENSE ├── README.md └── yzz_weixinapp ├── app.js ├── app.json ├── app.wxss ├── config.js ├── pages ├── asr │ ├── asr.js │ ├── asr.json │ ├── asr.wxml │ └── asr.wxss ├── index │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss ├── logs │ ├── logs.js │ ├── logs.json │ ├── logs.wxml │ └── logs.wxss └── test │ ├── test.js │ ├── test.json │ ├── test.wxml │ └── test.wxss ├── pics ├── bg.jpg ├── voice_icon_speech_sound_1.png ├── voice_icon_speech_sound_2.png ├── voice_icon_speech_sound_3.png ├── voice_icon_speech_sound_4.png └── voice_icon_speech_sound_5.png └── utils ├── GUID.js ├── MD5.js ├── NLI.js └── util.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | *.zip 18 | *.tar.gz 19 | *.rar 20 | 21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 22 | hs_err_pid* 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 happycxz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nlp_olami_yaozhizhi_wechat_littleapp_demo 2 | wechat little app, base on olami NLI inteface, named it as '遥知之' (yaozhizhi). 3 | 4 | 5 | ## 微信小程序智能生活小秘书开发详解 6 | 7 | 8 | **>>>>>>>>>>>>>>>>>>>>>>>> 欢迎转载 <<<<<<<<<<<<<<<<<<<<<<<<** 9 | 10 | **本文原地址:[http://blog.csdn.net/happycxz/article/details/75432928](http://blog.csdn.net/happycxz/article/details/75432928)** 11 | 12 | ### 扫码试用(左右皆可) 13 | 14 | ![小程序码小](http://img.blog.csdn.net/20170720103514998?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFwcHljeHo=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) ![二维码小](http://img.blog.csdn.net/20170720103917321?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFwcHljeHo=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 15 | 16 | 17 | **“遥知之”微信小程序完整源码下载:** 18 | 19 | 码云:[http://git.oschina.net/happycxz/nlp_olami_yaozhizhi_wechat_littleapp_demo](http://git.oschina.net/happycxz/nlp_olami_yaozhizhi_wechat_littleapp_demo) 20 | 21 | github: [https://github.com/happycxz/nlp_olami_yaozhizhi_wechat_littleapp_demo](https://github.com/happycxz/nlp_olami_yaozhizhi_wechat_littleapp_demo) 22 | 23 | ### 实现功能 24 | 25 | 实现一个智能生活信息查询的小秘书功能,支持查天气、新闻、日历、汇率、笑话、故事、百科、诗词、邮编、区号、菜谱、股票、节目预告,还支持闲聊、算24点、数学计算、单位换算、购物、搜索等功能。 26 | 27 | 使用方式: 28 | 29 | **新版上线支持语音识别,按下说话,松开发送。** 30 | 31 | 老版本上支持摇一摇、点界面按钮、手动输入、下拉刷新这四种方式。 32 | 33 | ### 界面展示 34 | 35 | ![新版本界面展示](http://img.blog.csdn.net/20170919112113110?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFwcHljeHo=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 36 | 37 | 38 | ### 开发资源 39 | 1. 免费开放语义接口平台 olami.ai 40 | 2. 微信小程序平台 41 | 3. js, css 42 | 43 | 更多代码描述及项目详情,请见原文链接: 44 | 45 | [我的微信小程序支持语音识别啦!“遥知之”不再装聋](http://blog.csdn.net/happycxz/article/details/78024986) 46 | 47 | [微信小程序——智能小秘“遥知之”源码分享(语义理解基于olami)](http://blog.csdn.net/happycxz/article/details/75432928) 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /yzz_weixinapp/app.js: -------------------------------------------------------------------------------- 1 | //app.js 2 | 3 | const corpusList = require('./config').corpus 4 | var UTIL = require('./utils/util.js'); 5 | 6 | App({ 7 | onShow: function () { 8 | UTIL.log('App Show') 9 | }, 10 | onHide: function () { 11 | UTIL.log('App Hide') 12 | }, 13 | onLaunch: function () { 14 | UTIL.log('App Launch') 15 | this.updateUserLocation() 16 | }, 17 | 18 | updateUserLocation: function() { 19 | var that = this 20 | wx.getLocation({ 21 | //type: 'wgs84', // gps原始坐标 22 | type: 'gcj02', //国家标准加密坐标 23 | success: function (res) { 24 | that.globalData.latitude = res.latitude 25 | that.globalData.longitude = res.longitude 26 | that.globalData.speed = res.speed 27 | //var accuracy = res.accuracy 28 | UTIL.log('REFRESH LOCATION: ' + that.globalData.latitude + ' | ' + that.globalData.longitude + ' , speed: ' + that.globalData.speed) 29 | }, 30 | fail: function(res) { 31 | UTIL.log('REFRESH LOCATION FAILED...') 32 | } 33 | }) 34 | }, 35 | 36 | getUserInfo:function(cb){ 37 | var that = this 38 | if(this.globalData.userInfo){ 39 | typeof cb == "function" && cb(this.globalData.userInfo) 40 | }else{ 41 | //调用登录接口 42 | wx.login({ 43 | success: function () { 44 | wx.getUserInfo({ 45 | success: function (res) { 46 | that.globalData.userInfo = res.userInfo 47 | that.globalData.custId = UTIL.getUserUnique(that.globalData.userInfo); 48 | typeof cb == "function" && cb(that.globalData.userInfo) 49 | } 50 | }) 51 | }, 52 | fail: function () { 53 | UTIL.log('登录WX失败了!') 54 | } 55 | }) 56 | } 57 | }, 58 | 59 | clearUserInfo: function() { 60 | var that = this 61 | that.globalData.userInfo = null; 62 | that.globalData.hasLogin = false; 63 | }, 64 | 65 | globalData:{ 66 | userInfo:null, 67 | corpus: corpusList, 68 | custId: '', 69 | latitude: 0.0, 70 | longitude: 0.0, 71 | speed: 0, 72 | } 73 | }) 74 | -------------------------------------------------------------------------------- /yzz_weixinapp/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/asr/asr", 4 | "pages/test/test", 5 | "pages/index/index", 6 | "pages/logs/logs" 7 | ], 8 | "window": { 9 | "backgroundTextStyle": "black", 10 | "navigationBarBackgroundColor": "#F8F8F8", 11 | "navigationBarTitleText": "遥知之 -- 人工智能小秘书", 12 | "navigationBarTextStyle": "black", 13 | "backgroundColor": "#F8F8F8", 14 | "enablePullDownRefresh": false 15 | }, 16 | "networkTimeout": { 17 | "request": 10000, 18 | "connectSocket": 10000, 19 | "uploadFile": 10000, 20 | "downloadFile": 10000 21 | } 22 | } -------------------------------------------------------------------------------- /yzz_weixinapp/app.wxss: -------------------------------------------------------------------------------- 1 | /**app.wxss**/ 2 | .container { 3 | height: 100%; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: center; 8 | box-sizing: border-box; 9 | } 10 | -------------------------------------------------------------------------------- /yzz_weixinapp/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 小程序配置文件 3 | */ 4 | 5 | module.exports = { 6 | 7 | //NLI appkey 8 | appkey: `b4118cd178064b45b7c8f1242bcde31f`, 9 | 10 | //NLI appsecret 11 | appsecret: `7908028332a64e47b8336d71ad3ce9ab`, 12 | 13 | corpus: [ 14 | // '闲聊', 15 | // '天气', 16 | // '诗词', 17 | // '单位换算', 18 | // '新闻', 19 | // '算24点', 20 | // '菜谱', 21 | // '汇率', 22 | // '邮编', 23 | // '区号', 24 | // '股票', 25 | // '日历', 26 | // '节目预告', 27 | // '笑话', 28 | // '故事', 29 | // '购物', 30 | // '数学运算', 31 | // '百科', 32 | // '搜索', 33 | 34 | '你今年多大啦', 35 | '上海今天天气如何', 36 | '北京的呢', 37 | '李白写过什么诗', 38 | '我要听李白的静夜思', 39 | '背一首将进酒', 40 | '一公里等于多少英尺', 41 | '我要看体育新闻', 42 | '4567算24点', 43 | '红烧肉的做法', 44 | '1美元能换算多少人民币', 45 | '查一下南昌的邮编', 46 | '郑州的区号是多少', 47 | '中国石油的股价', 48 | '今年中秋节是哪一天', 49 | '明晚湖南卫视放什么节目', 50 | '来个笑话', 51 | '讲个故事听听', 52 | '我要买电脑', 53 | '1加到100等于多少', 54 | '黄山有多高', 55 | '百度搜一下薛之谦的照片', 56 | ] 57 | }; 58 | -------------------------------------------------------------------------------- /yzz_weixinapp/pages/asr/asr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 作者:happycxz 3 | * 最后更新时间:2017.11.09 4 | * 源码分享链接:http://www.happycxz.com/m/?p=125 5 | * 6 | * https的silk语音识别API(专供微信小程序调用):https://api.happycxz.com/wxapp/silk2asr 7 | * https的mp3语音识别API(专供微信小程序调用):https://api.happycxz.com/wxapp/mp32asr 8 | * 该API服务搭建全过程解析及源码分享贴:http://blog.csdn.net/happycxz/article/details/78016299 9 | * 需要使用此API请联系作者QQ:404499164 10 | * 11 | * 遵循开放、分享、自由、免费的精神,把开源坚持到底 12 | */ 13 | 14 | //获取应用实例 15 | var app = getApp() 16 | 17 | var UTIL = require('../../utils/util.js'); 18 | var GUID = require('../../utils/GUID.js'); 19 | var NLI = require('../../utils/NLI.js'); 20 | 21 | const appkey = require('../../config').appkey 22 | const appsecret = require('../../config').appsecret 23 | 24 | //微信小程序新录音接口,录出来的是aac或者mp3,这里要录成mp3 25 | const mp3Recorder = wx.getRecorderManager() 26 | const mp3RecoderOptions = { 27 | duration: 60000, 28 | sampleRate: 16000, 29 | numberOfChannels: 1, 30 | encodeBitRate: 48000, 31 | format: 'mp3', 32 | //frameSize: 50 33 | } 34 | 35 | //弹幕定时器 36 | var timer; 37 | 38 | var pageSelf = undefined; 39 | 40 | var doommList = []; 41 | class Doomm { 42 | constructor() { 43 | this.text = UTIL.getRandomItem(app.globalData.corpus); 44 | this.top = Math.ceil(Math.random() * 40); 45 | this.time = Math.ceil(Math.random() * 8 + 6); 46 | this.color = getRandomColor(); 47 | this.display = true; 48 | let that = this; 49 | setTimeout(function () { 50 | doommList.splice(doommList.indexOf(that), 1); 51 | doommList.push(new Doomm()); 52 | 53 | pageSelf.setData({ 54 | doommData: doommList 55 | }) 56 | }, this.time * 1000) 57 | } 58 | } 59 | function getRandomColor() { 60 | let rgb = [] 61 | for (let i = 0; i < 3; ++i) { 62 | let color = Math.floor(Math.random() * 256).toString(16) 63 | color = color.length == 1 ? '0' + color : color 64 | rgb.push(color) 65 | } 66 | return '#' + rgb.join('') 67 | } 68 | 69 | Page({ 70 | data: { 71 | j: 1,//帧动画初始图片 72 | isSpeaking: false,//是否正在说话 73 | outputTxt : "", //输出识别结果 74 | 75 | doommData: [] 76 | }, 77 | 78 | initDoomm: function () { 79 | doommList.push(new Doomm()); 80 | doommList.push(new Doomm()); 81 | doommList.push(new Doomm()); 82 | this.setData({ 83 | doommData: doommList 84 | }) 85 | }, 86 | 87 | onLoad: function () { 88 | pageSelf = this; 89 | this.initDoomm(); 90 | 91 | //onLoad中为录音接口注册两个回调函数,主要是onStop,拿到录音mp3文件的文件名(不用在意文件后辍是.dat还是.mp3,后辍不决定音频格式) 92 | mp3Recorder.onStart(() => { 93 | UTIL.log('mp3Recorder.onStart()...') 94 | }) 95 | mp3Recorder.onStop((res) => { 96 | UTIL.log('mp3Recorder.onStop() ' + res) 97 | const { tempFilePath } = res 98 | var urls = "https://api.happycxz.com/wxapp/mp32asr"; 99 | UTIL.log('mp3Recorder.onStop() tempFilePath:' + tempFilePath) 100 | processFileUploadForAsr(urls, tempFilePath, this); 101 | }) 102 | }, 103 | 104 | /////////////////////////////////////////////////////////////// 以下是调用新接口实现的录音,录出来的是 mp3 105 | touchdown: function () { 106 | //touchdown_mp3: function () { 107 | UTIL.log("mp3Recorder.start with" + mp3RecoderOptions) 108 | var _this = this; 109 | speaking.call(this); 110 | this.setData({ 111 | isSpeaking: true 112 | }) 113 | mp3Recorder.start(mp3RecoderOptions); 114 | }, 115 | touchup: function () { 116 | //touchup_mp3: function () { 117 | UTIL.log("mp3Recorder.stop") 118 | this.setData({ 119 | isSpeaking: false, 120 | }) 121 | mp3Recorder.stop(); 122 | }, 123 | 124 | 125 | //切换到老版本 126 | turnToOld: function () { 127 | wx.navigateTo({ 128 | url: '../index/index', 129 | }) 130 | }, 131 | 132 | /////////////////////////////////////////////////////////////// 以下是调用老接口实现的录音,录出来的是 silk_v3 133 | //手指按下 134 | touchdown_silk: function () { 135 | //touchdown: function () { 136 | UTIL.log("手指按下了... new date : " + new Date) 137 | var _this = this; 138 | speaking.call(this); 139 | this.setData({ 140 | isSpeaking: true 141 | }) 142 | //开始录音 143 | wx.startRecord({ 144 | success: function (res) { 145 | //临时路径,下次进入小程序时无法正常使用 146 | var tempFilePath = res.tempFilePath; 147 | UTIL.log('record SUCCESS file path:' + tempFilePath) 148 | _this.setData({ 149 | recordPath: tempFilePath 150 | }); 151 | }, 152 | fail: function (res) { 153 | //录音失败 154 | wx.showModal({ 155 | title: '提示', 156 | content: '录音的姿势不对!', 157 | showCancel: false, 158 | success: function (res) { 159 | if (res.confirm) { 160 | UTIL.log('用户点击确定') 161 | return 162 | } 163 | } 164 | }) 165 | } 166 | }) 167 | }, 168 | //手指抬起 169 | touchup_silk: function () { 170 | //touchup: function () { 171 | UTIL.log("手指抬起了...") 172 | this.setData({ 173 | isSpeaking: false, 174 | }) 175 | clearInterval(this.timer) 176 | wx.stopRecord() 177 | 178 | var _this = this 179 | setTimeout(function () { 180 | var urls = "https://api.happycxz.com/wxapp/silk2asr/"; 181 | UTIL.log(_this.data.recordPath); 182 | processFileUploadForAsr(urls, _this.data.recordPath, _this); 183 | }, 1000) 184 | }, 185 | 186 | 187 | }) 188 | 189 | //上传录音文件到 api.happycxz.com 接口,处理语音识别和语义,结果输出到界面 190 | function processFileUploadForAsr(urls, filePath, _this) { 191 | wx.uploadFile({ 192 | url: urls, 193 | filePath: filePath, 194 | name: 'file', 195 | formData: { "appKey": appkey, "appSecret": appsecret, "userId": UTIL.getUserUnique() }, 196 | header: { 'content-type': 'multipart/form-data' }, 197 | success: function (res) { 198 | UTIL.log('res.data:' + res.data); 199 | 200 | var nliResult = getNliFromResult(res.data); 201 | UTIL.log('nliResult:' + nliResult); 202 | var stt = getSttFromResult(res.data); 203 | UTIL.log('stt:' + stt); 204 | 205 | var sentenceResult; 206 | try { 207 | sentenceResult = NLI.getSentenceFromNliResult(nliResult); 208 | } catch (e) { 209 | UTIL.log('touchup() 错误' + e.message + '发生在' + e.lineNumber + '行'); 210 | sentenceResult = '没明白你说的,换个话题?' 211 | } 212 | 213 | var lastOutput = "==>语音识别结果:\n" + stt + "\n\n==>语义处理结果:\n" + sentenceResult; 214 | _this.setData({ 215 | outputTxt: lastOutput, 216 | }); 217 | wx.hideToast(); 218 | }, 219 | fail: function (res) { 220 | UTIL.log(res); 221 | wx.showModal({ 222 | title: '提示', 223 | content: "网络请求失败,请确保网络是否正常", 224 | showCancel: false, 225 | success: function (res) { 226 | } 227 | }); 228 | wx.hideToast(); 229 | } 230 | }); 231 | } 232 | 233 | function getNliFromResult(res_data) { 234 | var res_data_json = JSON.parse(res_data); 235 | var res_data_result_json = JSON.parse(res_data_json.result); 236 | return res_data_result_json.nli; 237 | } 238 | 239 | function getSttFromResult(res_data) { 240 | var res_data_json = JSON.parse(res_data); 241 | var res_data_result_json = JSON.parse(res_data_json.result); 242 | return res_data_result_json.asr.result; 243 | } 244 | 245 | //麦克风帧动画 246 | function speaking() { 247 | var _this = this; 248 | //话筒帧动画 249 | var i = 1; 250 | this.timer = setInterval(function () { 251 | i++; 252 | i = i % 5; 253 | _this.setData({ 254 | j: i 255 | }) 256 | }, 200); 257 | } 258 | -------------------------------------------------------------------------------- /yzz_weixinapp/pages/asr/asr.json: -------------------------------------------------------------------------------- 1 | { 2 | "window": { 3 | "enablePullDownRefresh": false 4 | } 5 | } -------------------------------------------------------------------------------- /yzz_weixinapp/pages/asr/asr.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{outputTxt}} 5 | 6 | 7 | 8 | 9 | 语义理解基于olami.ai,作者QQ:404499164 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {{item.text}} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /yzz_weixinapp/pages/asr/asr.wxss: -------------------------------------------------------------------------------- 1 | /* pages/asr/asr.wxss */ 2 | 3 | page{ 4 | background-color:beige; 5 | background-image: url(http://img.blog.csdn.net/20170720105808995?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFwcHljeHo=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast); 6 | background-size: cover; 7 | } 8 | 9 | .page-section{ 10 | display: flex; 11 | flex-direction: column; 12 | margin-bottom: 10rpx; 13 | } 14 | 15 | .text-head{ 16 | color: #ff0000; 17 | font-size: 28rpx; 18 | align-items: center; 19 | margin-top: 5rpx; 20 | } 21 | 22 | .button-selection { 23 | display: flex; 24 | flex-direction: column; 25 | justify-content: center; 26 | align-items: center; 27 | } 28 | 29 | .button-selection2 { 30 | justify-content: space-between; 31 | align-content: space-between; 32 | flex-shrink:1; 33 | } 34 | 35 | .little-gap-top { 36 | margin-top: 10rpx; 37 | } 38 | 39 | .big-gap-top { 40 | margin-top: 100rpx; 41 | } 42 | 43 | .button-show { 44 | display: flex; 45 | align-self: center; 46 | justify-content: center; 47 | } 48 | 49 | .bottom-button { 50 | justify-content: space-around; 51 | flex-shrink:0; 52 | } 53 | 54 | .text-box{ 55 | margin-bottom: 0rpx; 56 | margin-left: 50rpx; 57 | margin-right: 50rpx; 58 | padding: 40rpx 0; 59 | display: flex; 60 | min-height: 650rpx; 61 | max-width: 600rpx; 62 | width:600rpx; 63 | background-color: #ffffff; 64 | justify-content: center; 65 | align-items: center; 66 | text-align: left; 67 | font-size: 30rpx; 68 | color: #353535; 69 | line-height: 2em; 70 | word-wrap: break-word; 71 | border: 1px solid cornflowerblue; 72 | } 73 | 74 | 75 | /* 录音 */ 76 | .speak-style{ 77 | position: relative; 78 | height: 240rpx; 79 | width: 240rpx; 80 | border-radius: 20rpx; 81 | margin: 0 auto; 82 | background: #26A5FF; 83 | } 84 | .record-style{ 85 | position: fixed; 86 | bottom: 0; 87 | left: 0; 88 | height: 120rpx; 89 | width: 100%; 90 | } 91 | .btn-style{ 92 | margin-left: 30rpx; 93 | margin-right: 30rpx; 94 | } 95 | 96 | .sound-style{ 97 | position: absolute; 98 | width: 74rpx; 99 | height:150rpx; 100 | margin-top: 45rpx; 101 | margin-left: 83rpx; 102 | } 103 | 104 | 105 | /* 弹幕 */ 106 | .button{ 107 | position: absolute; 108 | bottom: 0; 109 | width: 100%; 110 | } 111 | .aon{ 112 | position: absolute; 113 | white-space:nowrap; 114 | animation-timing-function: linear; 115 | animation-fill-mode: none; 116 | } 117 | .doommview{ 118 | z-index: 3; 119 | height: 80%; 120 | width: 100%; 121 | /* position: absolute; */ 122 | } 123 | 124 | @keyframes first{ 125 | from{left: 100%; } 126 | to{left: -100%;} 127 | } -------------------------------------------------------------------------------- /yzz_weixinapp/pages/index/index.js: -------------------------------------------------------------------------------- 1 | //获取应用实例 2 | var app = getApp() 3 | 4 | var UTIL = require('../../utils/util.js'); 5 | var GUID = require('../../utils/GUID.js'); 6 | var NLI = require('../../utils/NLI.js'); 7 | 8 | var cursor = 0; 9 | var lastYYYTime = new Date().getTime(); 10 | 11 | var domainCorpus = ''; 12 | var lastCorpus = ''; 13 | 14 | 15 | Page({ 16 | 17 | isShow: false, 18 | 19 | /** 20 | * 页面的初始数据 21 | */ 22 | data: { 23 | //调试后门 24 | isDbg: false, 25 | //输入框文本 26 | inputTxt: '', 27 | //输出框文本 28 | outputTxt: '' 29 | }, 30 | 31 | /** 32 | * 生命周期函数--监听页面加载 33 | */ 34 | onLoad: function (options) { 35 | UTIL.log('index.onLoad') 36 | //调用应用实例的方法获取全局数据 37 | app.getUserInfo(function (userInfo2) { 38 | UTIL.log('user unique 1: ' + UTIL.getUserUnique(userInfo2)) 39 | }) 40 | UTIL.log('user unique 2: ' + UTIL.getUserUnique(app.globalData.userInfo)) 41 | }, 42 | 43 | /** 44 | * 生命周期函数--监听页面初次渲染完成 45 | */ 46 | onReady: function () { 47 | UTIL.log('index.onReady') 48 | }, 49 | 50 | /** 51 | * 生命周期函数--监听页面显示 52 | */ 53 | onShow: function () { 54 | UTIL.log('index.onShow') 55 | 56 | var that = this; 57 | that.isShow = true; 58 | 59 | //调用重力加速度传感API模拟摇一摇接口 60 | wx.onAccelerometerChange(function (e) { 61 | 62 | if (!that.isShow) { 63 | //当前界面不显示时不应该调用 64 | return 65 | } 66 | 67 | if (isBong(e.x) && isBong(e.y)) { 68 | if (new Date().getTime() - lastYYYTime <= 2000) { 69 | //1秒限制摇一次,避免摇一下触发多次请求 70 | UTIL.log('摇的太频繁啦,请等2秒再摇!' + e.x + ', '+ e.y + ', ' + e.z); 71 | return; 72 | } 73 | 74 | //更新最后一次成功摇的时间戳 75 | lastYYYTime = new Date().getTime() 76 | 77 | //从语料库中挑选语料运行语义理解,显示结果 78 | var selectedCorpus = selectCorpusRunNli(that); 79 | 80 | //弹Toast窗提示当前刷到哪句语料 81 | wx.showToast({ 82 | title: selectedCorpus, 83 | icon: 'success', 84 | duration: 1500 85 | }); 86 | } 87 | }) 88 | }, 89 | 90 | /** 91 | * 生命周期函数--监听页面隐藏 92 | */ 93 | onHide: function () { 94 | UTIL.log('index.onHide') 95 | //页面隐藏后,关掉摇一摇检测 96 | wx.stopAccelerometer(); 97 | }, 98 | 99 | /** 100 | * 生命周期函数--监听页面卸载 101 | */ 102 | onUnload: function () { 103 | UTIL.log('index.onUnload') 104 | }, 105 | 106 | /** 107 | * 页面相关事件处理函数--监听用户下拉动作 108 | */ 109 | onPullDownRefresh: function () { 110 | UTIL.log('index.onPullDownRefresh') 111 | 112 | wx.stopPullDownRefresh(); 113 | 114 | //页面下拉,触发轮换语料理解 115 | selectCorpusRunNli(this) 116 | }, 117 | 118 | /** 119 | * 页面上拉触底事件的处理函数 120 | */ 121 | onReachBottom: function () { 122 | UTIL.log('index.onReachBottom') 123 | }, 124 | 125 | /** 126 | * 用户点击右上角分享 127 | */ 128 | onShareAppMessage: function () { 129 | UTIL.log('index.onShareAppMessage') 130 | 131 | wx.showToast({ 132 | title: '谢谢分享!', 133 | icon: 'success', 134 | duration: 1500 135 | }); 136 | }, 137 | 138 | switchChange: function (e) { 139 | UTIL.log('index.switchChange by:' + e.detail.value) 140 | }, 141 | 142 | //输入文本框聚焦触发清除输入框中的内容 143 | bindFocusClear: function(e) { 144 | UTIL.log('index.bindFocusClear') 145 | if (e.detail.value === '') { 146 | return; 147 | } 148 | 149 | UTIL.log('clear: ' + e.detail.value) 150 | var self = this; 151 | self.setData({ 152 | inputTxt: '' 153 | }); 154 | }, 155 | 156 | //点击完成按钮时触发 157 | bindConfirmControl: function(e) { 158 | var inputTxt = e.detail.value; 159 | UTIL.log('index.bindConfirmControl input string: ' + inputTxt); 160 | 161 | //手动打字输入语料运行语义理解 162 | singleCorpusRunNli(inputTxt, this) 163 | }, 164 | 165 | //测试按钮触发事件 166 | bindTest: function () { 167 | UTIL.log('index.bindTest') 168 | 169 | //测试按钮替代摇一摇,从语料库中选语料测试 170 | selectCorpusRunNli(this) 171 | }, 172 | 173 | //快捷按钮触发语料运行语义理解 174 | bindCorpusGenerator: function (e) { 175 | UTIL.log('index.bindCorpusGenerator') 176 | //获取"data-cp"中的语料 177 | var corpusList = e.target.dataset.cp.split('|'); 178 | 179 | //默认头一次(或新切换时)点击,选用第一句语料 180 | var corpus = corpusList[0]; 181 | if (domainCorpus !== corpusList[0]) { 182 | domainCorpus = corpusList[0]; 183 | } else { 184 | //否则在语料表中随机挑选一个 185 | corpus = UTIL.getRandomItem(corpusList); 186 | 187 | //与上一句重复就换一句 188 | if (lastCorpus === corpus) { 189 | corpus = UTIL.getRandomItem(corpusList); 190 | if (lastCorpus === corpus) { 191 | corpus = UTIL.getRandomItem(corpusList); 192 | if (lastCorpus === corpus) { 193 | corpus = UTIL.getRandomItem(corpusList); 194 | } 195 | } 196 | } 197 | } 198 | 199 | //记录最近一次使用的语料 200 | lastCorpus = corpus; 201 | 202 | singleCorpusRunNli(corpus, this); 203 | }, 204 | 205 | turnToNew: function() { 206 | wx.navigateBack({ 207 | }) 208 | } 209 | }) 210 | 211 | 212 | //处理NLI语义结果 213 | function NliProcess(corpus, self) { 214 | var nliResult; 215 | NLI.process(corpus, function (isSuccess, result) { 216 | if (isSuccess) { 217 | } else { 218 | 219 | } 220 | 221 | var sentenceResult; 222 | try { 223 | var resultJson = JSON.parse(result); 224 | nliResult = resultJson.nli; 225 | UTIL.log("NLI RESULT IS:" + nliResult); 226 | sentenceResult = NLI.getSentenceFromNliResult(nliResult); 227 | } catch (e) { 228 | UTIL.log('NliProcess() 错误' + e.message + '发生在' + e.lineNumber + '行'); 229 | sentenceResult = '没明白你说的,换个话题?' 230 | } 231 | 232 | typeof self !== 'undefined' && self.setData({ 233 | outputTxt: sentenceResult 234 | }) 235 | }) 236 | } 237 | 238 | //如输入是dbg且确认,则切换到debug模式,多显示两个按钮 239 | function singleCorpusRunNli(inputTxt, self) { 240 | if (inputTxt === 'dbg' || inputTxt === 'DBG' || inputTxt === 'Dbg') { 241 | self.setData({ 242 | //打开调试按钮 243 | isDbg: true, 244 | inputTxt: '' 245 | }); 246 | // 打开调试 247 | wx.setEnableDebug({ 248 | enableDebug: true 249 | }) 250 | return; 251 | } else if(inputTxt === 'gbd' || inputTxt === 'GBD' || inputTxt === 'Gbd') { 252 | self.setData({ 253 | //关闭调试按钮 254 | isDbg: false, 255 | inputTxt: '' 256 | }); 257 | // 关闭调试 258 | wx.setEnableDebug({ 259 | enableDebug: false, 260 | }) 261 | return; 262 | } 263 | 264 | //正常用户输入文本 265 | if (inputTxt !== '') { 266 | self.setData({ 267 | inputTxt: inputTxt 268 | }); 269 | NliProcess(inputTxt, self); 270 | } 271 | } 272 | 273 | //选择预置语料运行语义理解 274 | function selectCorpusRunNli(self) { 275 | var corpus = app.globalData.corpus; 276 | 277 | //顺序选择一句语料 278 | var corpusSelected = corpus[cursor] 279 | 280 | self.setData({ 281 | //更新页面input框显示 282 | inputTxt: corpusSelected 283 | }) 284 | 285 | UTIL.log('selected corpus:' + corpusSelected) 286 | //调用语料处理,刷新输出框结果 287 | NliProcess(corpusSelected, self); 288 | 289 | //光标后移,备下次挑选下一条语料 290 | if (cursor++ >= corpus.length - 1) { 291 | cursor = 0; 292 | } 293 | 294 | return corpusSelected; 295 | } 296 | 297 | //检测加速度传感器灵敏度 298 | function isBong(val) { 299 | //目前灵敏度设置为0.8,且摇一摇只监测了x和y轴变动,具体见调用此函数的地方 300 | if (val >= 0.8 || val <= -0.8) { 301 | return true; 302 | } 303 | 304 | return false; 305 | } -------------------------------------------------------------------------------- /yzz_weixinapp/pages/index/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "window": { 3 | "enablePullDownRefresh": true 4 | } 5 | } -------------------------------------------------------------------------------- /yzz_weixinapp/pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{outputTxt}} 5 | 6 | 7 | 8 | 9 | 语义理解基于“欧拉蜜”:cn.olami.ai 10 | 支持“摇一摇”、点按钮、手动输入、向下拉 11 | 12 | 13 | 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /yzz_weixinapp/pages/index/index.wxss: -------------------------------------------------------------------------------- 1 | page{ 2 | background-color:beige; 3 | background-image: url(http://img.blog.csdn.net/20170720105808995?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFwcHljeHo=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast); 4 | background-size: cover; 5 | } 6 | 7 | .page-section{ 8 | display: flex; 9 | flex-direction: column; 10 | margin-bottom: 10rpx; 11 | } 12 | 13 | .page-center { 14 | align-items: center; 15 | } 16 | 17 | .page-gap{ 18 | margin-top: 5rpx; 19 | } 20 | 21 | .button-selection { 22 | display: flex; 23 | flex-direction: column; 24 | justify-content: center; 25 | align-items: center; 26 | } 27 | 28 | .button-selection2 { 29 | justify-content: space-between; 30 | align-content: space-between; 31 | flex-shrink:1; 32 | } 33 | 34 | .little-gap-top { 35 | margin-top: 15rpx; 36 | } 37 | 38 | .little-gap-left { 39 | margin-left: 5rpx; 40 | } 41 | 42 | .text-description{ 43 | color: #ff0000; 44 | font-size: 28rpx; 45 | } 46 | 47 | .text-head{ 48 | color: #00BFFF; 49 | font-size: 36rpx; 50 | } 51 | 52 | .common-disappear { 53 | display: none 54 | } 55 | 56 | .button-show { 57 | display: flex; 58 | align-self: center; 59 | justify-content: center; 60 | } 61 | 62 | .text-box{ 63 | margin-bottom: 0rpx; 64 | margin-left: 50rpx; 65 | margin-right: 50rpx; 66 | padding: 40rpx 0; 67 | display: flex; 68 | min-height: 300rpx; 69 | max-width: 600rpx; 70 | width:600rpx; 71 | background-color: #ffffff; 72 | justify-content: center; 73 | align-items: center; 74 | text-align: left; 75 | font-size: 30rpx; 76 | color: #353535; 77 | line-height: 2em; 78 | word-wrap: break-word; 79 | border: 1px solid cornflowerblue; 80 | } 81 | 82 | .weui-cells { 83 | position: relative; 84 | margin-top: 1.17647059em; 85 | background-color: #DCDCDC; 86 | line-height: 1.41176471; 87 | font-size: 14px; 88 | } 89 | 90 | .weui-cells_after-title { 91 | margin-top: 0; 92 | } 93 | 94 | .weui-input { 95 | height: 2.58823529em; 96 | width: 17em; 97 | min-height: 2.58823529em; 98 | line-height: 2.58823529em; 99 | } 100 | 101 | .bottom-button { 102 | justify-content: space-around; 103 | flex-shrink:0; 104 | } 105 | -------------------------------------------------------------------------------- /yzz_weixinapp/pages/logs/logs.js: -------------------------------------------------------------------------------- 1 | //logs.js 2 | var util = require('../../utils/util.js') 3 | Page({ 4 | data: { 5 | logs: [] 6 | }, 7 | onLoad: function () { 8 | this.setData({ 9 | logs: (wx.getStorageSync('logs') || []).map(function (log) { 10 | var DATE = new Date(log); 11 | if (typeof DATE === 'date') { 12 | //当log是一个毫秒数,延用官方的案例,转成对应时间存LOG中(util.formatTime被我改成只输出时分秒) 13 | return util.formatTime(new Date(log)) 14 | } 15 | 16 | //非毫秒数时,记录LOG原始内容,不作转换 17 | return log; 18 | }) 19 | }) 20 | } 21 | }) 22 | -------------------------------------------------------------------------------- /yzz_weixinapp/pages/logs/logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "调试LOG页" 3 | } -------------------------------------------------------------------------------- /yzz_weixinapp/pages/logs/logs.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{index + 1}}. {{log}} 5 | 6 | 7 | -------------------------------------------------------------------------------- /yzz_weixinapp/pages/logs/logs.wxss: -------------------------------------------------------------------------------- 1 | .log-list { 2 | display: flex; 3 | flex-direction: column; 4 | padding: 40rpx; 5 | } 6 | .log-item { 7 | margin: 10rpx; 8 | } 9 | -------------------------------------------------------------------------------- /yzz_weixinapp/pages/test/test.js: -------------------------------------------------------------------------------- 1 | var page = undefined; 2 | 3 | var UTIL = require('../../utils/util.js'); 4 | 5 | Page({ 6 | onLoad: function () { 7 | page = this; 8 | 9 | wx.getSystemInfo({ 10 | success: function (res) { 11 | UTIL.log('system info:' + JSON.stringify(res)) 12 | } 13 | }) 14 | 15 | }, 16 | bindbt: function () { 17 | var timer = setInterval(this.addDoomm, 1000) 18 | }, 19 | data: { 20 | doommData: [] 21 | }, 22 | 23 | addDoomm : function() { 24 | if (doommList.length < 5) { 25 | doommList.push(new Doomm("弹幕测试程序", Math.ceil(Math.random() * 40 + 50), Math.ceil(Math.random() * 10 + 10), getRandomColor())); 26 | this.setData({ 27 | doommData: doommList 28 | }) 29 | } 30 | } 31 | }) 32 | 33 | var doommList = []; 34 | var i = 0; 35 | class Doomm { 36 | constructor(text, top, time, color) { 37 | this.text = text + i; 38 | this.top = top; 39 | this.time = time; 40 | this.color = color; 41 | this.display = true; 42 | let that = this; 43 | this.id = i++; 44 | setTimeout(function () { 45 | doommList.splice(doommList.indexOf(that), 1); 46 | page.setData({ 47 | doommData: doommList 48 | }) 49 | }, this.time * 1000) 50 | } 51 | } 52 | function getRandomColor() { 53 | let rgb = [] 54 | for (let i = 0; i < 3; ++i) { 55 | let color = Math.floor(Math.random() * 256).toString(16) 56 | color = color.length == 1 ? '0' + color : color 57 | rgb.push(color) 58 | } 59 | return '#' + rgb.join('') 60 | } -------------------------------------------------------------------------------- /yzz_weixinapp/pages/test/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "遥知之 -- 录音测试页" 3 | } -------------------------------------------------------------------------------- /yzz_weixinapp/pages/test/test.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{item.text}} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /yzz_weixinapp/pages/test/test.wxss: -------------------------------------------------------------------------------- 1 | /* pages/asr/asr.wxss */ 2 | 3 | page{ 4 | background-color:beige; 5 | background-image: url(http://img.blog.csdn.net/20170720105808995?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGFwcHljeHo=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast); 6 | background-size: cover; 7 | } 8 | 9 | .page-section{ 10 | display: flex; 11 | flex-direction: column; 12 | margin-bottom: 10rpx; 13 | } 14 | 15 | .text-head{ 16 | color: #ff0000; 17 | font-size: 28rpx; 18 | align-items: center; 19 | margin-top: 5rpx; 20 | } 21 | 22 | .button-selection { 23 | display: flex; 24 | flex-direction: column; 25 | justify-content: center; 26 | align-items: center; 27 | } 28 | 29 | .button-selection2 { 30 | justify-content: space-between; 31 | align-content: space-between; 32 | flex-shrink:1; 33 | } 34 | 35 | .little-gap-top { 36 | margin-top: 10rpx; 37 | } 38 | 39 | .big-gap-top { 40 | margin-top: 100rpx; 41 | } 42 | 43 | .button-show { 44 | display: flex; 45 | align-self: center; 46 | justify-content: center; 47 | } 48 | 49 | .bottom-button { 50 | justify-content: space-around; 51 | flex-shrink:0; 52 | } 53 | 54 | .text-box{ 55 | margin-bottom: 0rpx; 56 | margin-left: 50rpx; 57 | margin-right: 50rpx; 58 | padding: 40rpx 0; 59 | display: flex; 60 | min-height: 300rpx; 61 | max-width: 600rpx; 62 | width:600rpx; 63 | background-color: #ffffff; 64 | justify-content: center; 65 | align-items: center; 66 | text-align: left; 67 | font-size: 30rpx; 68 | color: #353535; 69 | line-height: 2em; 70 | word-wrap: break-word; 71 | border: 1px solid cornflowerblue; 72 | } 73 | 74 | 75 | /* 录音 */ 76 | .speak-style{ 77 | position: relative; 78 | height: 240rpx; 79 | width: 240rpx; 80 | border-radius: 20rpx; 81 | margin: 0 auto; 82 | background: #26A5FF; 83 | } 84 | .record-style{ 85 | position: fixed; 86 | bottom: 0; 87 | left: 0; 88 | height: 120rpx; 89 | width: 100%; 90 | } 91 | .btn-style{ 92 | margin-left: 30rpx; 93 | margin-right: 30rpx; 94 | } 95 | 96 | .sound-style{ 97 | position: absolute; 98 | width: 74rpx; 99 | height:150rpx; 100 | margin-top: 45rpx; 101 | margin-left: 83rpx; 102 | } 103 | 104 | 105 | /* 弹幕 */ 106 | .barrage-fly{ 107 | z-index: 3; 108 | height: 20%; 109 | width: 100%; 110 | position: absolute; 111 | top: 0; 112 | } 113 | .barrage-textFly{ 114 | position: absolute; 115 | white-space:nowrap; 116 | } 117 | 118 | /**index.wxss**/ 119 | .button{ 120 | position: absolute; 121 | bottom: 0; 122 | width: 100%; 123 | } 124 | .aon{ 125 | position: absolute; 126 | white-space:nowrap;/* 防止向下换行*/ 127 | animation-timing-function: linear; 128 | animation-fill-mode: none; 129 | } 130 | .doommview{ 131 | z-index: 3; 132 | height: 80%; 133 | width: 100%; 134 | position: absolute; 135 | } 136 | 137 | @keyframes first{ 138 | from{left: 100%; } 139 | to{left: -100%;} 140 | } 141 | -------------------------------------------------------------------------------- /yzz_weixinapp/pics/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happycxz/nlp_olami_yaozhizhi_wechat_littleapp_demo/b6fbec55302851cb623e9ae51144bea73ddc4514/yzz_weixinapp/pics/bg.jpg -------------------------------------------------------------------------------- /yzz_weixinapp/pics/voice_icon_speech_sound_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happycxz/nlp_olami_yaozhizhi_wechat_littleapp_demo/b6fbec55302851cb623e9ae51144bea73ddc4514/yzz_weixinapp/pics/voice_icon_speech_sound_1.png -------------------------------------------------------------------------------- /yzz_weixinapp/pics/voice_icon_speech_sound_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happycxz/nlp_olami_yaozhizhi_wechat_littleapp_demo/b6fbec55302851cb623e9ae51144bea73ddc4514/yzz_weixinapp/pics/voice_icon_speech_sound_2.png -------------------------------------------------------------------------------- /yzz_weixinapp/pics/voice_icon_speech_sound_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happycxz/nlp_olami_yaozhizhi_wechat_littleapp_demo/b6fbec55302851cb623e9ae51144bea73ddc4514/yzz_weixinapp/pics/voice_icon_speech_sound_3.png -------------------------------------------------------------------------------- /yzz_weixinapp/pics/voice_icon_speech_sound_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happycxz/nlp_olami_yaozhizhi_wechat_littleapp_demo/b6fbec55302851cb623e9ae51144bea73ddc4514/yzz_weixinapp/pics/voice_icon_speech_sound_4.png -------------------------------------------------------------------------------- /yzz_weixinapp/pics/voice_icon_speech_sound_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happycxz/nlp_olami_yaozhizhi_wechat_littleapp_demo/b6fbec55302851cb623e9ae51144bea73ddc4514/yzz_weixinapp/pics/voice_icon_speech_sound_5.png -------------------------------------------------------------------------------- /yzz_weixinapp/utils/GUID.js: -------------------------------------------------------------------------------- 1 | 2 | //表示全局唯一标识符 (GUID)。 3 | function Guid(g) { 4 | var arr = new Array(); //存放32位数值的数组 5 | 6 | if (typeof (g) == "string") { //如果构造函数的参数为字符串 7 | InitByString(arr, g); 8 | } else { 9 | InitByOther(arr); 10 | } 11 | 12 | //返回一个值,该值指示 Guid 的两个实例是否表示同一个值。 13 | this.Equals = function (o) { 14 | if (o && o.IsGuid) { 15 | return this.ToString() == o.ToString(); 16 | } else { 17 | return false; 18 | } 19 | } 20 | 21 | //Guid对象的标记 22 | this.IsGuid = function () { } 23 | //返回 Guid 类的此实例值的 String 表示形式。 24 | this.ToString = function (format) { 25 | if (typeof (format) == "string") { 26 | if (format == "N" || format == "D" || format == "B" || format == "P") { 27 | return ToStringWithFormat(arr, format); 28 | } else { 29 | return ToStringWithFormat(arr, "D"); 30 | } 31 | } else { 32 | return ToStringWithFormat(arr, "D"); 33 | } 34 | } 35 | 36 | //由字符串加载 37 | function InitByString(arr, g) { 38 | g = g.replace(/\{|\(|\)|\}|-/g, ""); 39 | g = g.toLowerCase(); 40 | if (g.length != 32 || g.search(/[^0-9,a-f]/i) != -1) { 41 | InitByOther(arr); 42 | } else { 43 | for (var i = 0; i < g.length; i++) { 44 | arr.push(g[i]); 45 | } 46 | } 47 | } 48 | 49 | //由其他类型加载 50 | function InitByOther(arr) { 51 | var i = 32; 52 | while (i--) { 53 | arr.push("0"); 54 | } 55 | } 56 | 57 | /* 58 | 根据所提供的格式说明符,返回此 Guid 实例值的 String 表示形式。 59 | N 32 位: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 60 | D 由连字符分隔的 32 位数字 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 61 | B 括在大括号中、由连字符分隔的 32 位数字:{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} 62 | P 括在圆括号中、由连字符分隔的 32 位数字:(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) 63 | */ 64 | 65 | function ToStringWithFormat(arr, format) { 66 | switch (format) { 67 | case "N": 68 | return arr.toString().replace(/,/g, ""); 69 | case "D": 70 | var str = arr.slice(0, 8) + "-" + arr.slice(8, 12) + "-" + arr.slice(12, 16) + "-" + arr.slice(16, 20) + "-" + arr.slice(20, 32); 71 | str = str.replace(/,/g, ""); 72 | return str; 73 | case "B": 74 | var str = ToStringWithFormat(arr, "D"); 75 | str = "{" + str + "}"; 76 | return str; 77 | case "P": 78 | var str = ToStringWithFormat(arr, "D"); 79 | str = "(" + str + ")"; 80 | return str; 81 | default: 82 | return new Guid(); 83 | } 84 | } 85 | } 86 | 87 | //Guid 类的默认实例,其值保证均为零。 88 | Guid.Empty = new Guid(); 89 | 90 | //初始化 Guid 类的一个新实例。 91 | Guid.NewGuid = function () { 92 | var g = ""; 93 | var i = 32; 94 | 95 | while (i--) { 96 | g += Math.floor(Math.random() * 16.0).toString(16); 97 | } 98 | return new Guid(g).ToString(); 99 | } 100 | 101 | module.exports = { 102 | NewGuid: Guid.NewGuid 103 | } -------------------------------------------------------------------------------- /yzz_weixinapp/utils/MD5.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | //md5加密算法 4 | function md5(string) { 5 | var x = Array(); 6 | var k, AA, BB, CC, DD, a, b, c, d; 7 | var S11 = 7, S12 = 12, S13 = 17, S14 = 22; 8 | var S21 = 5, S22 = 9, S23 = 14, S24 = 20; 9 | var S31 = 4, S32 = 11, S33 = 16, S34 = 23; 10 | var S41 = 6, S42 = 10, S43 = 15, S44 = 21; 11 | string = Utf8Encode(string); 12 | x = ConvertToWordArray(string); 13 | a = 0x67452301; 14 | b = 0xEFCDAB89; 15 | c = 0x98BADCFE; 16 | d = 0x10325476; 17 | for (k = 0; k < x.length; k += 16) { 18 | AA = a; 19 | BB = b; 20 | CC = c; 21 | DD = d; 22 | a = FF(a, b, c, d, x[k + 0], S11, 0xD76AA478); 23 | d = FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756); 24 | c = FF(c, d, a, b, x[k + 2], S13, 0x242070DB); 25 | b = FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE); 26 | a = FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF); 27 | d = FF(d, a, b, c, x[k + 5], S12, 0x4787C62A); 28 | c = FF(c, d, a, b, x[k + 6], S13, 0xA8304613); 29 | b = FF(b, c, d, a, x[k + 7], S14, 0xFD469501); 30 | a = FF(a, b, c, d, x[k + 8], S11, 0x698098D8); 31 | d = FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF); 32 | c = FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1); 33 | b = FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE); 34 | a = FF(a, b, c, d, x[k + 12], S11, 0x6B901122); 35 | d = FF(d, a, b, c, x[k + 13], S12, 0xFD987193); 36 | c = FF(c, d, a, b, x[k + 14], S13, 0xA679438E); 37 | b = FF(b, c, d, a, x[k + 15], S14, 0x49B40821); 38 | a = GG(a, b, c, d, x[k + 1], S21, 0xF61E2562); 39 | d = GG(d, a, b, c, x[k + 6], S22, 0xC040B340); 40 | c = GG(c, d, a, b, x[k + 11], S23, 0x265E5A51); 41 | b = GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA); 42 | a = GG(a, b, c, d, x[k + 5], S21, 0xD62F105D); 43 | d = GG(d, a, b, c, x[k + 10], S22, 0x2441453); 44 | c = GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681); 45 | b = GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8); 46 | a = GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6); 47 | d = GG(d, a, b, c, x[k + 14], S22, 0xC33707D6); 48 | c = GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87); 49 | b = GG(b, c, d, a, x[k + 8], S24, 0x455A14ED); 50 | a = GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905); 51 | d = GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8); 52 | c = GG(c, d, a, b, x[k + 7], S23, 0x676F02D9); 53 | b = GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A); 54 | a = HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942); 55 | d = HH(d, a, b, c, x[k + 8], S32, 0x8771F681); 56 | c = HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122); 57 | b = HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C); 58 | a = HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44); 59 | d = HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9); 60 | c = HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60); 61 | b = HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70); 62 | a = HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6); 63 | d = HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA); 64 | c = HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085); 65 | b = HH(b, c, d, a, x[k + 6], S34, 0x4881D05); 66 | a = HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039); 67 | d = HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5); 68 | c = HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8); 69 | b = HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665); 70 | a = II(a, b, c, d, x[k + 0], S41, 0xF4292244); 71 | d = II(d, a, b, c, x[k + 7], S42, 0x432AFF97); 72 | c = II(c, d, a, b, x[k + 14], S43, 0xAB9423A7); 73 | b = II(b, c, d, a, x[k + 5], S44, 0xFC93A039); 74 | a = II(a, b, c, d, x[k + 12], S41, 0x655B59C3); 75 | d = II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92); 76 | c = II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D); 77 | b = II(b, c, d, a, x[k + 1], S44, 0x85845DD1); 78 | a = II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F); 79 | d = II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0); 80 | c = II(c, d, a, b, x[k + 6], S43, 0xA3014314); 81 | b = II(b, c, d, a, x[k + 13], S44, 0x4E0811A1); 82 | a = II(a, b, c, d, x[k + 4], S41, 0xF7537E82); 83 | d = II(d, a, b, c, x[k + 11], S42, 0xBD3AF235); 84 | c = II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB); 85 | b = II(b, c, d, a, x[k + 9], S44, 0xEB86D391); 86 | a = AddUnsigned(a, AA); 87 | b = AddUnsigned(b, BB); 88 | c = AddUnsigned(c, CC); 89 | d = AddUnsigned(d, DD); 90 | } 91 | var temp = WordToHex(a) + WordToHex(b) + WordToHex(c) + WordToHex(d); 92 | return temp.toLowerCase(); 93 | } 94 | function RotateLeft(lValue, iShiftBits) { 95 | return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits)); 96 | } 97 | function AddUnsigned(lX, lY) { 98 | var lX4, lY4, lX8, lY8, lResult; 99 | lX8 = (lX & 0x80000000); 100 | lY8 = (lY & 0x80000000); 101 | lX4 = (lX & 0x40000000); 102 | lY4 = (lY & 0x40000000); 103 | lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF); 104 | if (lX4 & lY4) { 105 | return (lResult ^ 0x80000000 ^ lX8 ^ lY8); 106 | } 107 | if (lX4 | lY4) { 108 | if (lResult & 0x40000000) { 109 | return (lResult ^ 0xC0000000 ^ lX8 ^ lY8); 110 | } else { 111 | return (lResult ^ 0x40000000 ^ lX8 ^ lY8); 112 | } 113 | } else { 114 | return (lResult ^ lX8 ^ lY8); 115 | } 116 | } 117 | function F(x, y, z) { 118 | return (x & y) | ((~x) & z); 119 | } 120 | function G(x, y, z) { 121 | return (x & z) | (y & (~z)); 122 | } 123 | function H(x, y, z) { 124 | return (x ^ y ^ z); 125 | } 126 | function I(x, y, z) { 127 | return (y ^ (x | (~z))); 128 | } 129 | function FF(a, b, c, d, x, s, ac) { 130 | a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac)); 131 | return AddUnsigned(RotateLeft(a, s), b); 132 | } 133 | function GG(a, b, c, d, x, s, ac) { 134 | a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac)); 135 | return AddUnsigned(RotateLeft(a, s), b); 136 | } 137 | function HH(a, b, c, d, x, s, ac) { 138 | a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac)); 139 | return AddUnsigned(RotateLeft(a, s), b); 140 | } 141 | function II(a, b, c, d, x, s, ac) { 142 | a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac)); 143 | return AddUnsigned(RotateLeft(a, s), b); 144 | } 145 | function ConvertToWordArray(string) { 146 | var lWordCount; 147 | var lMessageLength = string.length; 148 | var lNumberOfWords_temp1 = lMessageLength + 8; 149 | var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64; 150 | var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16; 151 | var lWordArray = Array(lNumberOfWords - 1); 152 | var lBytePosition = 0; 153 | var lByteCount = 0; 154 | while (lByteCount < lMessageLength) { 155 | lWordCount = (lByteCount - (lByteCount % 4)) / 4; 156 | lBytePosition = (lByteCount % 4) * 8; 157 | lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition)); 158 | lByteCount++; 159 | } 160 | lWordCount = (lByteCount - (lByteCount % 4)) / 4; 161 | lBytePosition = (lByteCount % 4) * 8; 162 | lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition); 163 | lWordArray[lNumberOfWords - 2] = lMessageLength << 3; 164 | lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29; 165 | return lWordArray; 166 | } 167 | function WordToHex(lValue) { 168 | var WordToHexValue = "", WordToHexValue_temp = "", lByte, lCount; 169 | for (lCount = 0; lCount <= 3; lCount++) { 170 | lByte = (lValue >>> (lCount * 8)) & 255; 171 | WordToHexValue_temp = "0" + lByte.toString(16); 172 | WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2); 173 | } 174 | return WordToHexValue; 175 | } 176 | function Utf8Encode(string) { 177 | var utftext = ""; 178 | for (var n = 0; n < string.length; n++) { 179 | var c = string.charCodeAt(n); 180 | if (c < 128) { 181 | utftext += String.fromCharCode(c); 182 | } else if ((c > 127) && (c < 2048)) { 183 | utftext += String.fromCharCode((c >> 6) | 192); 184 | utftext += String.fromCharCode((c & 63) | 128); 185 | } else { 186 | utftext += String.fromCharCode((c >> 12) | 224); 187 | utftext += String.fromCharCode(((c >> 6) & 63) | 128); 188 | utftext += String.fromCharCode((c & 63) | 128); 189 | } 190 | } 191 | return utftext; 192 | } 193 | module.exports = { 194 | md5: md5 195 | } -------------------------------------------------------------------------------- /yzz_weixinapp/utils/NLI.js: -------------------------------------------------------------------------------- 1 | /* 2 | 对接olami免费开放语义平台的NLI接口 3 | */ 4 | 5 | //获取应用实例 6 | var app = getApp() 7 | 8 | const appkey = require('../config').appkey 9 | const appsecret = require('../config').appsecret 10 | 11 | var MD5 = require('./MD5.js'); 12 | var UTIL = require('./util.js'); 13 | 14 | const requestUrl = "https://cn.olami.ai/cloudservice/api"; 15 | const api = "nli"; 16 | 17 | 18 | function getLocationJson() { 19 | var lat = app.globalData.latitude; 20 | var lng = app.globalData.longitude; 21 | if (lat < 180 && lng < 180) { 22 | lat *= 1000000; 23 | lng *= 1000000; 24 | } 25 | 26 | return { "position_type": 0, "longitude": lng, "latitude": lat }; 27 | } 28 | 29 | //NLI接口访问 30 | //参数:corpus为句子;cb为回调函数(带两个参数,第一个参数表示调用NLI是否成功,第二个参数是NLI的返回数据) 31 | function process(corpus, cb) { 32 | var timestamp = new Date().getTime(); 33 | var originalSign = appsecret + "api=" + api + "appkey=" + appkey + "timestamp=" + timestamp + appsecret; 34 | var sign = MD5.md5(originalSign); 35 | 36 | var locationJson = getLocationJson(); 37 | var rqdataJson = { "data": { "input_type": 1, "text": corpus, "location": locationJson }, "data_type": "stt" }; 38 | var rqdata = JSON.stringify(rqdataJson); 39 | 40 | UTIL.log('input:' + rqdata + '\r\n, custId:' + app.globalData.custId + ', originalSign:' + originalSign); 41 | 42 | wx.request({ 43 | url: requestUrl, 44 | data: { 45 | appkey: appkey, 46 | api: api, 47 | timestamp: timestamp, 48 | sign: sign, 49 | rq: rqdata, 50 | cusid: app.globalData.custId, 51 | }, 52 | header: { 53 | 'content-type': 'application/x-www-form-urlencoded' 54 | }, 55 | method: 'POST', 56 | success: function (result) { 57 | var data = result.data.data; 58 | var jsonData = JSON.stringify(data); 59 | typeof cb == "function" && cb(true, jsonData) 60 | }, 61 | fail: function ({errMsg}) { 62 | typeof cb == "function" && cb(false, jsonData) 63 | } 64 | }) 65 | } 66 | 67 | //解析NLI接口返回的数据,从语义结果中筛选出适合显示的文本内容 68 | function getSentenceFromNliResult(nliArray) { 69 | var sentence; 70 | try { 71 | for (var i = 0; i < nliArray.length; i++) { 72 | var singleResult = nliArray[i]; 73 | sentence = singleResult.desc_obj.result; 74 | 75 | var content = ''; 76 | var appName = singleResult.type; 77 | //0: normal, 1: selection, 9 : openweb 78 | var tagType = 0; 79 | if (appName === 'selection') { 80 | appName = singleResult.desc_obj.type; 81 | tagType = 1; 82 | } else if (appName === 'openweb') { 83 | //appName = singleResult.desc_obj.type; 84 | tagType = 9; 85 | } 86 | switch (appName) { 87 | case 'joke': 88 | case 'story': 89 | sentence = singleResult.data_obj[0].content; 90 | break; 91 | case 'poem': 92 | if (tagType === 1) { 93 | for (var k = 0; k < singleResult.data_obj.length; k++) { 94 | content += '第' + (k + 1) + '个: ' + singleResult.data_obj[k].author + ' ' + singleResult.data_obj[k].poem_name + '\n'; 95 | } 96 | } 97 | break; 98 | case 'cooking': 99 | if (tagType === 1) { 100 | for (var k = 0; k < singleResult.data_obj.length; k++) { 101 | content += '第' + (k + 1) + '个: ' + singleResult.data_obj[k].name + '\n'; 102 | } 103 | } else if (tagType === 0) { 104 | content = singleResult.data_obj[0].content; 105 | } 106 | break; 107 | case 'baike': 108 | var filedNames = singleResult.data_obj[0].field_name; 109 | var filedValues = singleResult.data_obj[0].field_value; 110 | for (var k = 0; k < filedNames.length; k++) { 111 | content += filedNames[k] + ' : ' + filedValues[k] + '\n'; 112 | } 113 | break; 114 | case 'news': 115 | if (tagType === 1) { 116 | for (var k = 0; k < singleResult.data_obj.length; k++) { 117 | content += singleResult.data_obj[k].title + '\n'; 118 | content += singleResult.data_obj[k].detail + '…………\n'; 119 | content += '【欲看此条新闻详情请说第' + (k + 1) + '个,或拷此链接到浏览器中打开:' + singleResult.data_obj[k].ref_url + '】\n\n'; 120 | } 121 | } else if (tagType === 0) { 122 | content = singleResult.data_obj[0].detail; 123 | } 124 | break; 125 | case 'stock': 126 | var nowHour = new Date().getHours(); 127 | var isKaiPan = (nowHour >= 9) && (nowHour <= 15); 128 | for (var k = 0; k < singleResult.data_obj.length; k++) { 129 | content += singleResult.data_obj[k].name + ' : ' + singleResult.data_obj[k].id + '\n'; 130 | content += '开盘:' + singleResult.data_obj[k].price_start + '\n'; 131 | if (isKaiPan) { 132 | content += '现价(' + UTIL.getHHMMSS(singleResult.data_obj[k].time) + '):' + singleResult.data_obj[k].cur_price + '\n'; 133 | } else { 134 | content += '收盘:' + singleResult.data_obj[k].price_end + '\n'; 135 | } 136 | content += '成交量:' + singleResult.data_obj[k].volume + '\n'; 137 | content += '成交额:' + singleResult.data_obj[k].amount + '\n'; 138 | content += '最高价:' + singleResult.data_obj[k].price_high + '\n\n'; 139 | } 140 | break; 141 | case 'tvprogram': 142 | for (var k = 0; k < singleResult.data_obj.length; k++) { 143 | content += singleResult.data_obj[k].time + ' ' + singleResult.data_obj[k].name + '\n'; 144 | } 145 | break; 146 | case 'openweb': 147 | content += '【拷此链接到浏览器中打开:' + singleResult.data_obj[0].url + '】\n'; 148 | break; 149 | defalt: 150 | break; 151 | } 152 | if (content !== '') { 153 | sentence += '\n\n' + content; 154 | } 155 | UTIL.log('NLI返回sentence:' + sentence) 156 | } 157 | } catch (e) { 158 | UTIL.log('getSentenceFromNliResult() 错误' + e.message + '发生在' + e.lineNumber + '行'); 159 | sentence = '没明白你说的,换个话题?' 160 | } 161 | 162 | if (typeof sentence === 'undefined' || sentence === '') { 163 | sentence = '没明白你说的什么意思'; 164 | } 165 | 166 | return sentence; 167 | } 168 | 169 | 170 | module.exports = { 171 | process: process, 172 | getSentenceFromNliResult: getSentenceFromNliResult, 173 | } -------------------------------------------------------------------------------- /yzz_weixinapp/utils/util.js: -------------------------------------------------------------------------------- 1 | //获取应用实例 2 | var app = getApp() 3 | 4 | var Guid = require('./GUID.js'); 5 | 6 | var uuidSaved = ''; 7 | 8 | //将date格式的数据转成 hh:mm:ss 字符串格式 9 | function formatTime(date) { 10 | var year = date.getFullYear() 11 | var month = date.getMonth() + 1 12 | var day = date.getDate() 13 | 14 | var hour = date.getHours() 15 | var minute = date.getMinutes() 16 | var second = date.getSeconds() 17 | 18 | 19 | //return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':') 20 | return [hour, minute, second].map(formatNumber).join(':') 21 | } 22 | 23 | function formatNumber(n) { 24 | n = n.toString() 25 | return n[1] ? n : '0' + n 26 | } 27 | 28 | //log接口封装 29 | function log(obj) { 30 | console.log('<' + formatTime(new Date(Date.now())) + '>' + ' ' + obj) 31 | 32 | //往用户缓存去写 33 | // var logs = wx.getStorageSync('logs') || [] 34 | // logs.unshift('[' + formatTime(new Date(Date.now())) + ']' + ' ' + obj) 35 | // wx.setStorageSync('logs', logs) 36 | } 37 | 38 | //获取用户唯一标识,NLI接口中要上传用户唯一标识,这里获取:第一次登录时生成的uuid+微信号所在地+昵称 39 | function getUserUnique(userInfo) { 40 | //从缓存中读取uuid 41 | if (typeof uuidSaved === 'undefined' || uuidSaved === '') { 42 | var tmpUuid = wx.getStorageSync('uuid'); 43 | if (typeof tmpUuid === 'undefined' || tmpUuid === '') { 44 | uuidSaved = Guid.NewGuid(); 45 | wx.setStorageSync('uuid', uuidSaved); 46 | } else { 47 | uuidSaved = tmpUuid; 48 | } 49 | } 50 | 51 | var unique = uuidSaved; 52 | if (userInfo != null) { 53 | unique += '_' + userInfo.province + '_' + userInfo.nickName; 54 | } 55 | log('getUserUnique() return:' + unique) 56 | return unique; 57 | } 58 | 59 | //由 毫秒数字符串 获取 HH:mm:ss 格式时间 60 | function getHHMMSS(dateStr) { 61 | var dateVal = new Date(parseInt(dateStr)); 62 | var hh = dateVal.getHours() 63 | var mm = dateVal.getMinutes() 64 | var ss = dateVal.getSeconds() 65 | 66 | return ((hh >= 10) ? hh : '0' + hh) + ':' + ((mm >= 10) ? mm : '0' + mm) + ':' + ((ss >= 10) ? ss : '0' + ss) 67 | } 68 | 69 | 70 | //从语料数组中随机挑选一条语料 71 | function getRandomItem(corpusList) { 72 | var ret = corpusList[0]; 73 | ret = corpusList[Math.floor(Math.random() * corpusList.length)]; 74 | return ret; 75 | } 76 | 77 | module.exports = { 78 | formatTime: formatTime, 79 | log: log, 80 | getUserUnique: getUserUnique, 81 | getHHMMSS: getHHMMSS, 82 | getRandomItem: getRandomItem, 83 | } 84 | --------------------------------------------------------------------------------