小さなプログラムパスワードの赤い封筒機能を作る方法

小さなプログラムのバックエンドサポートを行う過程で、私は多くの興味深い機能に遭遇しました。思考を分散し、問題を解決する実際の能力を比較する人もいます。ここに抜粋と記録があります。最高です。

最初にいくつかの設計図を配置して、おおよその機能を確認します。

1

2

3

4

5

6

7

8

これはおそらく事実です。

図からわかるように、関係する少し複雑な関数ポイントは、音声認識とテキスト認識、赤いエンベロープ配布アルゴリズム、周辺の赤いエンベロープアルゴリズムなどです。残りは単純なCRUD操作です。CODING + TESTINGを1週間ほど使用しましたが、各機能ポイントの一般的な考え方と実装方法は次のとおりです。

音声認識:

この機能の適用シナリオは、ユーザーAがパスワードの赤い封筒を中国語で設定し、赤い封筒を受け取ったユーザーBがパスワードを音声で話す必要があり、それらが完全に一致する場合、一定量の赤い封筒が取得されます。

録音はアプレットによって提供されるネイティブインターフェイスと呼ばれますが、ここでさらに注意すべき点は、WeChatの録音形式が.silkであることです。オンラインで検索する方法は、.silk形式をwavまたはMP3形式に変換し、主要なクラウドサービスプラットフォームのインターフェイスを呼び出して音声認識機能を実現することです。

ここで使用 https://github.com/kn007/silk ...提供されたライブラリを使用してwav形式に変換し、Baiduの音声認識オープンインターフェイスを使用しますhttps://ai.baidu.com/tech/spe ...音声結果を認識します。

ビジネス実現の手順は次のとおりです。

1.フロントエンドが録音機能を実装します
。2.アップロードインターフェイスが.silk音声ファイルをアップロードしてライブラリに入ります。3
.音声認識タスクをトリガーし、成功をフロントエンドに返します(非同期)
。4.フロントエンドが認識結果をポーリングします。

アップロードから認識までの時間のかかる操作であるため、認識プロセスは非同期操作であることが望ましい。(ステップ3)

アップロード音声インターフェースの部分コード:

// ... 业务代码略
$voice = $this->getCreatedVoiceByBody(); // 上传并入库
$this->identifyVoice($voice); // 触发语音识别task
 
// ...
 
public function identifyVoice($voice)
{
    WorkerUtil::sendTaskByRouteAndParams('task/detectvoice', ['voiceid' => $voice->id, 'type' =>'redpack']);
}

上記のように、音声ファイルのアドレスを含むレコードIDとタイプがバックエンドタスクサービスに送信されます。

バックエンドタスクサービスは次のように処理されます。

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);
            }
        }
    }
    ...
}

タスクサービスの処理ロジックも非常に明確です。識別が必要なボイスIDを受け取り、レコードを検索し、音声ファイルをローカルのtmpディレクトリにダウンロードし、シェルを呼び出して形式を変換し、変換された形式を呼び出してBaiduの音声インターフェイスを呼び出して識別します。倉庫保管。

音声テーブルの構造は次のとおりです。

画像の説明

このようにして、音声認識機能が完成する。

赤い封筒の配布

適用シナリオ:赤い封筒を作成する場合

赤い封筒を開くには、一般的に2つの配布方法があります。1つは、作成時に各共有を割り当てることです。1つは、オープン時の動的割り当てであり、最初の1つはここで採用されます。

具体的な議論は以下にあります:https://www.zhihu.com/questio ...が見つかりました。

正直に言うと、この回答を読んだ後も、WeChatの赤いエンベロープアーキテクチャの実装、配布の記述など、いくつかのことを学びました。

私たちのアプリケーションはWeChatほどの大きさを持っていないため、当然、あまり多くのこと(負荷、同時実行性など)を考慮する必要はなく、製品の要件は、金額がWeChatの赤いエンベロープ配布方法に割り当てられる必要があるということだけです。そのため、拡張性、パフォーマンス、時間を考慮して、陳鵬の回答の文言をそのまま採用しましたが、PHP版になりました。さらに、redisは赤いエンベロープ共有のストレージとして、また同時実行問題の解決策として使用されます。

最初にコードに移動します(redpack / create):

$redpack = $this->getCreatedRedPackByBody();
// ... 业务逻辑代码略
// 设置随机红包份额
$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;
}

コードのこの部分のロジックは比較的単純で、主に次のとおりです。

現在の量とコピー数を関数(getRandomMoney)に渡し、現在のランダム量を計算した後、その量をredisのリスト(key = redpack:id)に書き込んでから、合計量と合計コピー数を差し引きます。 、減るまで。

注意すべき点がいくつかあります。

1.元の回答の乱数生成方法ではjava.math.BigDecimalを使用していますが、PHPには対応する関数がなく、それに付属する乱数は使いにくいものです。ここで使用される自己作成の乱数生成方法(6つの乱数を取得し、それらをビット数で除算して0.608948のような乱数を取得します)
2.各赤いエンベロープ共有の有効期限は1日です。赤い封筒満了の機能を実現するためです。

redisの結果(ポイント単位):

15元10元

画像の説明

100元で7:

画像の説明

50元で25元:

画像の説明

ランダムな割り当てが基本的に実現され、幸運の要件も考慮されていることがわかります。

使い方も簡単で、赤い封筒を開いて共有する場合は、このリストの左側を使用して、1つずつポップします。

赤い封筒地図

アプリケーションシナリオ:投稿された赤い封筒を見る

この実装の鍵は、周囲の座標アルゴリズムです。まず、赤いエンベロープを作成するときに緯度と経度の座標を取得することが前提条件であり、これはフロントエンドによって実装され、記録するだけで済みます。

次に、このインターフェイスを呼び出すときに、ユーザーの現在の緯度と経度を渡します。この緯度と経度に基づいて周辺範囲を計算し、この周辺範囲のレコードをテーブルで検索します。

コードは次のとおりです。

/**
*
* @param double $lng 经度
* @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 经度
* @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];
}

キーはgetAroundByCoordinatesアルゴリズムです。これは、入力された緯度、経度、および範囲に基づいて、左上、左下、右上、および右下隅の座標を計算します。それらがマップ上にマークされている場合、それらは長方形の範囲です。

興味があれば、 http://lbs.qq.com/tool/getpoint/このツールでは、ランダムに座標を取り、上記の方法に従って4つのコーナーを計算し、それが$ raidusによって指定された範囲と正確に一致するかどうかを確認できます。

このメソッドは私が作成したものではありませんが、それがどこから来たのか本当に覚えていません。Javaの実装方法をphpに変更したことを覚えています。元の作者に申し訳ありません。

おすすめ

転載: www.cnblogs.com/10manongit/p/12728002.html