摘要:語音識別該功能的應用場景是用戶設置了一個中文的口令紅包,接收到該紅包的用戶需要用語音說出該口令,完全匹配的話則獲取該紅包的某個比例金額。表結(jié)構(gòu)如下如此,便完成了語音識別功能。
在做小程序后端支持的過程中遇到不少有意思的功能,有些比較考你的思維散發(fā)及解決問題的實際能力,這里摘錄一下記錄下來,是為拋磚引玉、如能幫到別人,自然是最好不過了。
先放幾張設計圖看下大概功能:
大概便是如此。
通過圖片可以看到,涉及到的稍微復雜一點的功能點有:語音文字識別、紅包分配算法,周邊紅包算法等等。 其余的都是些簡單的CRUD操作。我CODING+TESTING用了差不多一周,以下說下各個功能點的大概實現(xiàn)思路及方法。
語音識別:該功能的應用場景是:A用戶設置了一個中文的口令紅包,接收到該紅包的B用戶需要用語音說出該口令,完全匹配的話則獲取該紅包的某個比例金額。
錄音自然是調(diào)用小程序提供的原生接口,不過這里比較坑的是微信的錄音格式是 .silk。網(wǎng)上搜索的方法是先將.silk格式轉(zhuǎn)成wav或者MP3格式,然后再調(diào)用各大云服務平臺的接口實現(xiàn)語音識別功能。
這里使用了 https://github.com/kn007/silk... 提供的庫用來轉(zhuǎn)成wav格式,然后使用百度的語音識別開放接口 https://ai.baidu.com/tech/spe... 來識別語音結(jié)果。
業(yè)務實現(xiàn)步驟如下:
1.前端實現(xiàn)錄音功能
2.upload接口上傳.silk語音文件,入庫
3.觸發(fā)語音識別task,返回成功給前端(異步)
4.前端輪詢識別結(jié)果。
因為從上傳到識別到返回結(jié)果是一個耗時操作,所以識別過程最好是異步操作。(第三步)
upload語音接口部分代碼:
// ... 業(yè)務代碼略 $voice = $this->getCreatedVoiceByBody(); // 上傳并入庫 $this->identifyVoice($voice); // 觸發(fā)語音識別task // ... public function identifyVoice($voice) { WorkerUtil::sendTaskByRouteAndParams("task/detectvoice", ["voiceid" => $voice->id, "type" =>"redpack"]); }
如上可見,將一條包含了語音文件地址的記錄id及類型發(fā)送到了后端task服務。
后端task服務處理如下:
class DetectVoice extends Action { public function run($voiceid, $type = "redpack") { if ($type == "redpack") { $voice = Voices::findOne($voiceid); $url = $voice->voice; $saveName = "/runtime/redpack-".$voiceid.".silk"; $convertName = "/runtime/redpack-".$voiceid.".wav"; } $this->saveToLocalByRemoteVoiceUrlAndLocalFileName($url, $saveName); $cfg = [ "appKey" => "xxx", "appSecret" => "xxx", "appId" => "xxx", ]; $util = new BaiduVoiceUtil($cfg); $code = exec("bash /www/silk-v3-decoder/converter.sh {$saveName} wav"); if ($code == 0) { $result = $util->asr($convertName); if ($result["err_no"] == 0) { $voicesResult = json_encode($result["result"], JSON_UNESCAPED_UNICODE); $voice->result = $voicesResult; $voice->save(); @unlink($saveName); @unlink($convertName); } } } ... }
task服務的處理邏輯也很清晰:接收需要識別的voiceid,查找記錄,把語音文件下到本地某個tmp目錄,調(diào)用shell轉(zhuǎn)換格式,將轉(zhuǎn)換后的格式調(diào)用baidu的語音接口進行識別,再將結(jié)果入庫。
voice表結(jié)構(gòu)如下:
如此,便完成了語音識別功能。
紅包分配應用場景:創(chuàng)建紅包時
打開紅包一般有兩種分配方法,一種是使用創(chuàng)建時便分配好每一份的份額。一種是打開時再動態(tài)分配,這里采取的是第一種。
具體討論可在知乎:https://www.zhihu.com/questio... 找到。
說實話,看完這個答案還是學到了一些東西的,如微信紅包的架構(gòu)實現(xiàn),分配寫法等等。
因為我們的應用沒有微信的量級,自然不需要考慮太多(負載,并發(fā)等),產(chǎn)品的要求也只是說金額這方面要實現(xiàn)類微信紅包的分配方法即可。因此,考慮到擴展及性能以及時間,分配寫法我直接采用了 陳鵬 的答案里的寫法,不過是變成了PHP的版本。并且搭配了redis 作為紅包份額的存儲及可能的并發(fā)問題處理方案。
先上代碼(redpack/create):
$redpack = $this->getCreatedRedPackByBody(); // ... 業(yè)務邏輯代碼略 // 設置隨機紅包份額 $this->setRedPackOpenOdds($redpack); protected function setRedPackOpenOdds($rp) { $remainNum = $rp->num; $remainMoney = $rp->fee; $key = "redpack:".$rp->id; $redis = yii::$app->redis; while (!empty($remainNum)) { $money = $this->getRandomMoney($remainNum, $remainMoney); $redis->executeCommand("RPUSH", [$key, $money]); } $redis->executeCommand("expire", [$key, 259200]); } protected function getRandomMoney(&$remainNum, &$remainMoney) { if ($remainNum == 1) { $remainNum--; return $remainMoney; } $randomNum = StringUtil::getRandom(6, 1); $seed = $randomNum / 1000000; $min = 1; $max = $remainMoney / $remainNum * 2; $money = $seed * $max; $money = $money <= $min ? $min : ceil($money); $remainNum--; $remainMoney -= $money; return $money; }
這部分代碼邏輯也相對簡單,主要就是:
將當前金額和份數(shù)傳入函數(shù)( getRandomMoney),在計算出當次的隨機金額后,將該金額寫入redis的一個list (key=redpack:id),然后將總金額和總份數(shù)減去,一直減完為止。
有幾點值得注意的地方:
1.原答案里的隨機數(shù)生成法使用了 java.math.BigDecimal. 可php沒有對應的函數(shù),自帶的隨機數(shù)也不好用。這里用的自己寫的隨機數(shù)生成方法 (獲取6位的隨機數(shù)字,然后除以它們的位數(shù),就得到類似于 0.608948的隨機數(shù))
2.每個紅包的份額設置了一天的過期時間,這是為了實現(xiàn)紅包過期的功能。
redis里的結(jié)果(單位為分):
10元分配15個
100元分配7個:
50元分配25個:
可以看到基本實現(xiàn)了隨機分配,也兼顧了手氣最佳的要求。
使用也簡單,打開紅包獲取份額的時候,使用這個list左邊一個個出棧就行了。
紅包地圖應用場景:查看周圍發(fā)布的紅包
這個實現(xiàn)的關(guān)鍵之處就是周邊的坐標算法。首先,前提條件是創(chuàng)建紅包時要獲取到經(jīng)緯度坐標,這個交由前端實現(xiàn),我們只記錄即可。
然后在調(diào)用這個接口時,把用戶當前的經(jīng)緯度傳過來。根據(jù)這個經(jīng)緯度計算出周邊范圍,然后查找表中在這個周邊范圍的記錄即可。
代碼如下:
/** * * @param double $lng 經(jīng)度 * @param double $lat 緯度 * @param integer $radius 范圍 * @return array */ public function run($lng, $lat, $radius = 500) { $coordinates = $this->getAroundByCoordinates($lng, $lat, $radius); $field = "id,lat,lng"; $data = (new Query()) ->select($field) ->from("{{app_redpack}}") ->where(sprintf("`lat` BETWEEN %f AND %f AND `lng` BETWEEN %f AND %f AND `ishandle` = 1 AND `isexpire` = 0", $coordinates[0], $coordinates[2], $coordinates[1], $coordinates[3])) ->all(); return ResponseUtil::getOutputArrayByCodeAndData(Api::SUCCESS, $data); } /** * 地球的圓周是24901英里。 * 24,901/360度 = 69.17 英里 / 度 * @param double $longitude 經(jīng)度 * @param double $latitude 緯度 * @param integer $raidus 范圍。單位米。 * @return array */ public function getAroundByCoordinates($longitude, $latitude, $raidus) { (double) $degree = (24901 * 1609) / 360.0; (double) $dpmLat = 1 / $degree; (double) $radiusLat = $dpmLat * $raidus; (double) $minLat = $latitude - $radiusLat; (double) $maxLat = $latitude + $radiusLat; (double) $mpdLng = $degree * cos($latitude * (pi() / 180)); (double) $dpmLng = 1 / $mpdLng; (double) $radiusLng = $dpmLng * $raidus; (double) $minLng = $longitude - $radiusLng; (double) $maxLng = $longitude + $radiusLng; return [$minLat, $minLng, $maxLat, $maxLng]; }
關(guān)鍵就是getAroundByCoordinates 這個算法,它根據(jù)輸入的經(jīng)緯度及范圍大小,計算出左上,左下,右上,右下四個角的坐標,在地圖上標出來的話就是 一個長方形的范圍。
有興趣的可以根據(jù) http://lbs.qq.com/tool/getpoint/ 這個工具,隨意點取一個坐標,根據(jù)以上的方法算出四個角,看看是不是剛好是$raidus指定的范圍。
需要說明的是這個方法不是我寫的,但是我實在不記得出處在哪了。我只是記得把java的實現(xiàn)方法改成了php。對原作者說聲抱歉。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/25778.html
摘要:本文以開發(fā)一個類似語音口令紅包小程序為例,向您講解如何使用阿里云函數(shù)計算快速構(gòu)建微信小程序的服務端。 前言 這篇文章適合所有的想微信小程序開發(fā)新手、老鳥以及想準備學習開發(fā)微信小程序的程序猿。本文以開發(fā)一個類似語音口令紅包小程序為例,向您講解如何使用阿里云函數(shù)計算快速構(gòu)建微信小程序的服務端。通過本文,您將會了解以下內(nèi)容: demo概覽 傳統(tǒng)服務器架構(gòu) VS Serverless架構(gòu) S...
摘要:本文以開發(fā)一個類似語音口令紅包小程序為例,向您講解如何使用阿里云函數(shù)計算快速構(gòu)建微信小程序的服務端。 前言 這篇文章適合所有的想微信小程序開發(fā)新手、老鳥以及想準備學習開發(fā)微信小程序的程序猿。本文以開發(fā)一個類似語音口令紅包小程序為例,向您講解如何使用阿里云函數(shù)計算快速構(gòu)建微信小程序的服務端。通過本文,您將會了解以下內(nèi)容: demo概覽 傳統(tǒng)服務器架構(gòu) VS Serverless架構(gòu) S...
摘要:摘要阿里云函數(shù)計算是一個事件驅(qū)動的全托管計算服務。微信小程序是一種不需要下載安裝即可使用的應用,它可以在微信內(nèi)被便捷地獲取和傳播。本文以開發(fā)一個類似語音口令紅包小程序為例,向您講解如何使用阿里云函數(shù)計算快速構(gòu)建微信小程序的服務端。 摘要: 阿里云函數(shù)計算是一個事件驅(qū)動的全托管計算服務。通過函數(shù)計算,您無需管理服務器等基礎設施,只需編寫代碼并上傳。微信小程序是一種不需要下載安裝即可使用的...
摘要:摘要阿里云函數(shù)計算是一個事件驅(qū)動的全托管計算服務。微信小程序是一種不需要下載安裝即可使用的應用,它可以在微信內(nèi)被便捷地獲取和傳播。本文以開發(fā)一個類似語音口令紅包小程序為例,向您講解如何使用阿里云函數(shù)計算快速構(gòu)建微信小程序的服務端。 摘要: 阿里云函數(shù)計算是一個事件驅(qū)動的全托管計算服務。通過函數(shù)計算,您無需管理服務器等基礎設施,只需編寫代碼并上傳。微信小程序是一種不需要下載安裝即可使用的...
閱讀 1572·2023-04-26 00:25
閱讀 950·2021-09-27 13:36
閱讀 956·2019-08-30 14:14
閱讀 2208·2019-08-29 17:10
閱讀 1035·2019-08-29 15:09
閱讀 1974·2019-08-28 18:21
閱讀 996·2019-08-26 13:27
閱讀 999·2019-08-26 10:58