当前位置: 首页 > 后端技术 > PHP

如何制作小程序密码红包功能

时间:2023-03-29 17:45:43 PHP

在制作小程序后台支持的过程中,遇到了很多有趣的功能。其中一些测试你的思维能力和解决问题的实践能力。如果能帮到别人就最好了。先放几张设计图看看大概功能:大概是这样。从图中可以看出,涉及的稍微复杂的功能包括:语音和文字识别、红包分发算法、包围红包算法等,其余都是简单的CRUD操作。使用CODING+TESTING快一周了,下面说说各个功能点的大致实现思路和方法。语音识别:该功能的应用场景为:用户A设置了一个带有中文密码的红包,收到红包的用户B需要用语音说出密码,如果完全匹配则获得一定比例的红包。自然而然的,录音就是调用小程序提供的原生接口,但是这里比较棘手的是,微信的录音格式是.silk。网上搜索的方法是先将.silk格式转换成wav或MP3格式,然后调用各大云服务平台的接口,实现语音识别功能。这里我们使用https://github.com/kn007/silk...https://github.com/kn007/silk...提供的库转换成wav格式,然后用百度的语音识别打开界面https://ai.baidu.com/tech/spe...识别语音结果。业务实现步骤如下:1.前端实现录音功能。2.上传接口上传.silk语音文件,放入仓库。3.触发语音识别任务,向前端返回成功(异步)。由于从上传到识别再到返回结果是一个耗时的操作,所以识别过程最好是异步操作。(第三步)上传部分语音接口代码://...业务代码略$voice=$this->getCreatedVoiceByBody();//上传并存储$this->identifyVoice($voice);//触发语音识别任务//...publicfunctionidentifyVoice($voice){WorkerUtil::sendTaskByRouteAndParams('task/detectvoice',['voiceid'=>$voice->id,'type'=>'redpack']);}从上面可以看出,一个包含语音文件地址的记录id和类型被发送到后端任务服务。后端任务服务处理如下:classDetectVoiceextendsAction{publicfunctionrun($voiceid,$type='redpack'){if($type=='redpack'){$voice=Voices::findOne($voiceid);$url=$voice->语音;$saveName='/runtime/redpack-'.$voiceid.'.silk';$convertName='/runtime/redpack-'.$voiceid.'.wav';}$this->saveToLocalByRemoteVoiceUrlAndLocalFileName($url,$saveName);$cfg=['appKey'=>'xxx','appSecret'=>'xxx','appId'=>'xxx',];$util=newBaiduVoiceUtil($cfg);$code=exec("bash/www/silk-v3-decoder/converter.sh{$saveName}wav");如果($code==0){$result=$util->asr($convertName);如果($result['err_no']==0){$voicesResult=json_encode($result['result'],JSON_UNESCAPED_UNICODE);$voice->result=$voicesResult;$语音->save();@unlink($saveName);@unlink($convertName);}}}...}任务服务的处理逻辑也很清晰:接收到需要识别的voiceid,搜索记录,下载语音文件到本地tmp目录,调用shell转换格式,调用百度的语音接口识别转换后的格式,然后将结果存入数据库。语音表的结构如下:这样就完成了语音识别功能。红包分配应用场景:创建红包时开红包一般有两种分配方式。一种是使用创建时分配给每个共享的共享。一种是打开的时候动态分配,这里用的是第一种。具体的讨论可以在知乎上找到:https://www.zhihu.com/questio...说实话,看了这个回答,还是学到了一些东西,比如微信红包的架构实现,写法分布等等。因为我们的应用没有微信的量级,自然不需要考虑太多(负载、并发等),产品需求也只是说在量上,我们需要实现一个类微信的红包发放方法。因此,考虑到扩展、性能和时间,我直接采用了陈鹏回答中的写法,但变成了PHP版本。并且搭载了redis作为红包份额的存储以及可能的并发问题处理方案。代码优先(redpack/create):$redpack=$this->getCreatedRedPackByBody();//...业务逻辑代码省略//设置随机红包份额$this->setRedPackOpenOdds($redpack);保护函数setRedPackOpenOdds($rp){$remainNum=$rp->num;$remainMoney=$rp->费用;$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]);}保护函数getRandomMoney(&$remainNum,&$remainMoney){if($remainNum==1){$remainNum--;返回$remainMoney;}$randomNum=StringUtil::getRandom(6,1);$seed=$randomNum/1000000;$分钟=1;$max=$remainMoney/$remainNum*2;$money=$seed*$max;$money=$money<=$min?$min:ceil($money);$剩余数--;$remainMoney-=$money;return$money;}这部分代码逻辑比较简单,主要是:将当前金额和股数传入函数(getRandomMoney),计算出当前随机金额后,将金额写入redis的一个列表(key=redpack:id),然后将总金额和total减去份数,在减完之前有几点值得注意:1.原答案中的随机数生成方法使用了java.math.BigDecimal。但是PHP中没有对应的函数,自带的随机数也不行。使用。这里使用了自己写的随机数生成方法(得到6位随机数,然后除以它们的位数得到一个类似0.608948的随机数)2.每个红包的份额有一个过期时间为有一天,这就是实现红包过期的功能。redis中的结果(以点数为单位):10元分配15块,100元分配7块:50元分配25块:可以看出基本实现了随机分配,同时也考虑了好运的要求帐户。它也很容易使用。打开红包获取分享时,只需使用此列表的左侧从堆栈中弹出一个堆栈即可。红包图应用场景:查看周围张贴的红包。这个实现的关键是周围坐标算法。首先,前提条件是在创建红包时获取经纬度坐标。这个是前端实现的,我们只需要记录一下即可。然后在调用这个接口的时候,传递用户当前的经纬度。根据经纬度计算周围范围,然后在表中查找周围范围内的记录。代码如下:/****@paramdouble$lnglongitude*@paramdouble$latlatitude*@paraminteger$radiusrange*@returnarray*/publicfunctionrun($lng,$lat,$radius=500){$坐标=$this->getAroundByCoordinates($lng,$lat,$radius);$field='id,lat,lng';$data=(newQuery())->select($field)->from('{{app_redpack}}')->where(sprintf("`lat`BETWEEN%fAND%fAND`lng`BETWEEN%fAND%fAND`ishandle`=1AND`isexpire`=0",$coordinates[0],$coordinates[2],$coordinates[1],$coordinates[3]))->all();returnResponseUtil::getOutputArrayByCodeAndData(Api::SUCCESS,$data);}/***地球的周长是24901英里。*24,901/360度=69.17英里/度*@paramdouble$longitude经度*@paramdouble$latitude纬度*@paraminteger$raidus范围。单位:米*@return数组*/publicfunctiongetAroundByCoordinates($longitude,$latitude,$raidus){(double)$degree=(24901*1609)/360.0;(双)$dpmLat=1/$degree;(双)$radiusLat=$dpmLat*$raidus;(双)$minLat=$latitude-$radiusLat;(双)$maxLat=$latitude+$radiusLat;(双)$mpdLng=$degree*cos($latitude*(pi()/180));(双)$dpmLng=1/$mpdLng;(双)$radiusLng=$dpmLng*$raidus;(双)$minLng=$longitude-$radiusLng;(双)$maxLng=$经度+$radiusLng;return[$minLat,$minLng,$maxLat,$maxLng];}关键是getAroundByCoordinates算法,根据输入的经纬度和范围大小计算左上角、左下角、右上角、右下角。坐标,如果标记在地图上,是一个矩形范围。有兴趣的可以用工具http://lbs.qq.com/tool/getpoint/随机点一个坐标,按照上面的方法计算四个角,看是不是刚好在指定的范围内$突袭。需要说明的是,这个方法不是我写的,但是我实在不记得是从哪里来的了。才想起把java的实现方式改成php的。对不起原作者。