Récemment, une fonction d'enveloppe rouge de mot de passe WeChat était sur le point d'être utilisée. Le patron s'est soudainement inquiété. Il a dit que la fête de la laine est si endémique maintenant, s'il ne fait pas attention, l'enveloppe rouge promotionnelle de 100 000 sera-t-elle épuisée en deux. jours ?... Alors pouvons-nous créer une fonction pour vérifier si la même personne reçoit l'enveloppe rouge ? N'est-ce pas suffisant ? Il lui tapota la tête et continua : « Tom, s'il te plaît, fais une reconnaissance d'empreinte vocale pour nous !
Le processus spécifique est le suivant :
Utilisateurs enregistrés Voiceprint (rendu final)
Connexion par empreinte vocale (rendu final)
Identification du fichier de téléchargement :
fil de discussion pm2
Étant donné que le fournisseur de services de reconnaissance d'empreintes vocales ne peut pas utiliser directement le client pour appeler directement et que l'audio n'est pas pris en charge, il est nécessaire de développer son propre serveur pour se connecter.
Pile technologique koa + co-wecaht-api + mysql + ffmpeg + pm2 + knex
Remarque : étant donné que le fournisseur de services ne prend pas en charge les fichiers amr WeChat, vous devez utiliser ffmpeg pour transcoder les fichiers amr audio WeChat en wav.
Ce qui suit est un code pertinent, ouvert. .
Développement WeChat jssdk Si vous êtes déjà familier avec l'API WeChat, passez à la section suivante
Obtenez le jeton WeChat
var api = await new WechatAPI (
config . appid ,
config . appsecret ,
async ( ) => {
// 传入一个获取全局token的方法
var txt = await fs . readFile ( "./token/access_token.txt" , "utf8" ) ;
return JSON . parse ( txt ) ;
} ,
async token => {
// 请将token存储到全局,跨进程、跨机器级别的全局,比如写到数据库、redis等
// 这样才能在cluster模式及多机情况下使用,以下为写入到文件的示例
await fs . writeFile ( "./token/access_token.txt" , JSON . stringify ( token ) ) ;
}
) ;
Remarque : Si le fichier de jeton ne peut pas être lu, créez manuellement un nouveau fichier texte dans le répertoire correspondant, tel que access_token.txt.
Obtenez la signature WeChat
var jsapi_ticket = await api . getLatestTicket ( ) ;
let nonce_str = 'abcdefg' ; // 密钥,字符串任意,可以随机生成
let timestamp = parseInt ( new Date ( ) . getTime ( ) / 1000 ) + '' ; // 时间戳
let url = ctx . request . body . url ; // 使用接口的url链接,不包含#后的内容
let str = 'jsapi_ticket=' + jsapi_ticket . ticket + '&noncestr=' + nonce_str + '×tamp=' + timestamp + '&url=' + url ;
let signature = sha1 ( str ) ;
ctx . body = {
appId : config . appid ,
timestamp : timestamp ,
nonceStr : nonce_str ,
signature : signature
}
demande inter-domaines
const Koa = require ( "koa" ) ;
const app = new Koa ( ) ;
const cors = require ( "koa-cors" ) ;
... . .
app . use (
cors ( {
origin : "http://www.xxxx.com" ,
maxAge : 5 ,
credentials : true ,
allowMethods : [ "OPTIONS" , "GET" , "POST" , "DELETE" ] ,
allowHeaders : [ 'Content-Type' , 'Accept' ]
} )
) ;
transcodage ffmpeg
const ffmpeg = require ( 'fluent-ffmpeg' ) ;
... .
var command = ffmpeg ( _delPath . amr )
. audioBitrate ( '16k' ) //16k音频采样率
. audioFrequency ( 16000 ) //16比特音频信号
. audioQuality ( 10 ) //音频质量
. on ( 'end' , function ( ) {
console . log ( 'file has been converted succesfully' ) ;
resolve ( ) ;
} )
. on ( 'error' , function ( err ) {
reject ( err . message )
console . log ( 'an error happened: ' + err . message ) ;
} )
. save ( _delPath . fix ) ;
Soumettre le serveur d'empreinte vocale
const rp = require ( "request-promise" ) ;
... . .
var vprData = {
method : "POST" ,
url : "http://www.xxxx.com" ,
headers : {
"cache-control" : "no-cache" ,
"x-udid" : "xxxxxx" ,
"x-session-key" : "xxxx" ,
"x-task-config" : "xxxxxx" ,
"x-request-date" : "xxxxxx" ,
"x-sdk-version" : "5.1" ,
"x-app-key" : "xxxxxxx"
} ,
formData : {
// Like <input type="file" name="file">
file : {
value : fs . createReadStream ( soundData . path ) ,
options : {
filename : soundData . name ,
contentType : soundData . type //mp3 = audio/mpeg, wav = audio/wav
}
}
}
} ;
var xml = await rp ( vprData ) ;
//xml to json
var resJson = { } ;
var parseString = require ( 'xml2js' ) . parseString ;
await new Promise ( ( resolve , reject ) => {
parseString ( xml . toString ( ) , async ( err , result ) => {
resJson = result . ResponseInfo ;
//do something
resolve ( ) ;
} ) ;
} ) ;
Pile technologique vue + vue-router + axios.
Supprimez le bouton contextuel copié en appuyant longuement sur WeChat
mounted ( ) {
document . oncontextmenu = function ( e ) {
e . preventDefault ( ) ;
} ;
//初始化 微信jssdk
vm . wx_init ( ) ;
}
Obtenez la signature WeChat et inscrivez-vous à l'événement
wx . config ( {
debug : false , // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId : res . appId , // 必填,公众号的唯一标识
timestamp : res . timestamp , // 必填,生成签名的时间戳
nonceStr : res . nonceStr , // 必填,生成签名的随机串
signature : res . signature , // 必填,签名,见附录1
jsApiList : [
"onMenuShareTimeline" ,
"onMenuShareAppMessage" ,
"uploadVoice" ,
"startRecord" ,
"playVoice" ,
"stopRecord" ,
"onVoicePlayEnd"
] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
} ) ;
Inviter l'utilisateur à l'avance à autoriser la fonction d'enregistrement afin d'éviter d'être invité à obtenir une autorisation au même moment où l'enregistrement est officiellement démarré. À ce moment, l'état de la fonction d'enregistrement est hors de contrôle.
if ( ! localStorage . rainAllowRecord || localStorage . rainAllowRecord !== "true" ) {
wx . startRecord ( {
success : function ( ) {
localStorage . rainAllowRecord = "true" ;
wx . stopRecord ( ) ;
} ,
cancel : function ( ) {
alert ( "用户拒绝授权录音" ) ;
}
} ) ;
}
OK, parler ne coûte rien, montre-toi le code.
Comment utiliser
git clone https: //github.com/ssttm169/tom-vpr.git
cd server
npm i / yarn
npm run dev //本地开发
npm start //服务器跑
//或者
cd client
npm i / yarn
npm run dev //本地开发
npm start //服务上跑客户端