How to make a small program password red envelope function

In the process of doing back-end support for small programs, I have encountered many interesting functions. Some compare your actual ability to distribute thinking and solve problems. Here is an excerpt to record it. It is to help others. If it can help others, naturally it is It ’s best.

First put a few design drawings to see the approximate function:

1

2

3

4

5

6

7

8

This is probably the case.

As you can see from the picture, the slightly more complicated function points involved are: speech and text recognition, red envelope distribution algorithm, peripheral red envelope algorithm and so on. The rest are simple CRUD operations. I used CODING + TESTING for almost a week. The following is a general idea and method of implementing each function point.

Speech Recognition:

The application scenario of this function is: user A sets a password red envelope in Chinese, and user B who receives the red envelope needs to speak the password in voice, and if they match exactly, a certain amount of the red envelope will be obtained.

The recording is naturally called the native interface provided by the applet, but what is more pitted here is that the recording format of WeChat is .silk. The method of searching online is to convert the .silk format to wav or MP3 format, and then call the interfaces of the major cloud service platforms to realize the voice recognition function.

Used here https://github.com/kn007/silk...The provided library is used to convert to wav format, and then use Baidu's speech recognition open interfacehttps: //ai.baidu.com/tech/spe ... to recognize voice results.

The business realization steps are as follows:

1. The front-end implements the recording function
2. The upload interface uploads the .silk voice file and enters the library
3. Triggers the voice recognition task and returns success to the front-end (asynchronous)
4. The front-end polls the recognition result.

Because it is a time-consuming operation from uploading to recognition to returning the result, the recognition process is preferably an asynchronous operation. (third step)

Partial code of upload voice interface:

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

As you can see above, a record id and type containing the address of the voice file is sent to the back-end task service.

The back-end task service is processed as follows:

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

The processing logic of the task service is also very clear: receive the voiceid that needs to be identified, search for the record, download the voice file to a local tmp directory, call the shell to convert the format, call the converted format to call Baidu's voice interface to identify, and then Warehousing.

The voice table structure is as follows:

image description

In this way, the voice recognition function is completed.

Red envelope distribution

Application scenario: when creating a red envelope

There are generally two distribution methods for opening red envelopes. One is to allocate each share when it is created. One is dynamic allocation when it is opened, and the first one is adopted here.

The specific discussion can be found in:https: //www.zhihu.com/questio ... found.

To be honest, after reading this answer, I still learned some things, such as the implementation of the WeChat red envelope architecture, the distribution writing, and so on.

Because our application does not have the magnitude of WeChat, naturally we do not need to consider too much (load, concurrency, etc.), and the product requirements are only to say that the amount of money must be allocated to the WeChat red envelope distribution method. Therefore, considering the expansion, performance, and time, I directly adopted the wording in Chen Peng's answer, but it became the PHP version. In addition, redis is used as a storage for red envelope shares and a solution for possible concurrency problems.

First go to the code (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;
}

The logic of this part of the code is relatively simple, mainly:

Pass the current amount and number of copies into the function (getRandomMoney), after calculating the current random amount, write the amount into a list of redis (key = redpack: id), and then subtract the total amount and total number of copies , Until it has been reduced.

There are a few points worth noting:

1. The random number generation method in the original answer uses java.math.BigDecimal. But PHP does not have a corresponding function, and the random number that comes with it is not easy to use. The self-written random number generation method used here (take 6 random numbers and then divide them by the number of bits to get a random number similar to 0.608948)
2. Each red envelope share has an expiration time of one day, which It is to realize the function of red envelope expiration.

The results in redis (in points):

15 yuan for 10 yuan

image description

7 for 100 yuan:

image description

25 yuan for 50 yuan:

image description

It can be seen that the random allocation is basically realized, and the requirement of best luck is also taken into account.

It is also simple to use. When opening the red envelope to get shares, just use the left side of this list to pop out one by one.

Red envelope map

Application scenario: View the red envelopes posted around

The key to this implementation is the surrounding coordinate algorithm. First, the prerequisite is to obtain the latitude and longitude coordinates when creating the red envelope. This is implemented by the front end, and we only need to record.

Then when calling this interface, pass the current latitude and longitude of the user. Calculate the peripheral range based on this latitude and longitude, and then look up the records in this peripheral range in the table.

code show as below:

/**
*
* @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];
}

The key is the getAroundByCoordinates algorithm. It calculates the coordinates of the upper left, lower left, upper right, and lower right corners based on the entered latitude and longitude and the size of the range. If it is marked on the map, it is a rectangular range.

If you are interested, you can http://lbs.qq.com/tool/getpoint/ This tool, you can randomly take a coordinate, calculate the four corners according to the above method, and see if it is exactly the range specified by $ raidus.

It should be noted that this method was not written by me, but I really don't remember where it came from. I just remember to change the java implementation method to php. Sorry to the original author.

Guess you like

Origin www.cnblogs.com/10manongit/p/12728002.html