如何做一个小程序口令红包功能

转载 2018年01月10日 00:00:00

在做小程序后端支持的过程中遇到不少有意思的功能,有些比较考你的思维散发及解决问题的实际能力,这里摘录一下记录下来,是为抛砖引玉、如能帮到别人,自然是最好不过了。

先放几张设计图看下大概功能:




大概便是如此。

通过图片可以看到,涉及到的稍微复杂一点的功能点有:语音文字识别、红包分配算法,周边红包算法等等。 其余的都是些简单的CRUD操作。我CODING+TESTING用了差不多一周,以下说下各个功能点的大概实现思路及方法。

语音识别

应用场景:A用户设置了一个中文的口令红包,接收到该红包的B用户需要用语音说出该口令,完全匹配的话则获取该红包的某个比例金额。

录音自然是调用小程序提供的原生接口,不过这里比较坑的是微信的录音格式是 .silk。网上搜索的方法是先将.silk格式转成wav或者MP3格式,然后再调用各大云服务平台的接口实现语音识别功能。

这里使用了 https://github.com/kn007/silk-v3-decoder 提供的库用来转成wav格式,然后使用百度的语音识别开放接口 https://ai.baidu.com/tech/speech/asr 来识别语音结果。

业务实现步骤如下:

1.前端实现录音功能 2.upload接口上传.silk语音文件,入库 3.触发语音识别task,返回成功给前端(异步) 4.前端轮询识别结果。

因为从上传到识别到返回结果是一个耗时操作,所以识别过程最好是异步操作。(第三步)

upload语音接口部分代码:

  1. // ... 业务代码略

  2. $voice = $this->getCreatedVoiceByBody(); // 上传并入库

  3. $this->identifyVoice($voice); // 触发语音识别task

  4. // ...

  5. public function identifyVoice($voice)

  6. {

  7.    WorkerUtil::sendTaskByRouteAndParams('task/detectvoice', ['voiceid' => $voice->id, 'type' =>'redpack']);

  8. }

如上可见,将一条包含了语音文件地址的记录id及类型发送到了后端task服务。

后端task服务处理如下:

  1. class DetectVoice extends Action

  2. {

  3.    public function run($voiceid, $type = 'redpack')

  4.    {

  5.        if ($type == 'redpack') {

  6.            $voice = Voices::findOne($voiceid);

  7.            $url = $voice->voice;

  8.            $saveName = '/runtime/redpack-'.$voiceid.'.silk';

  9.            $convertName = '/runtime/redpack-'.$voiceid.'.wav';

  10.        }

  11.        $this->saveToLocalByRemoteVoiceUrlAndLocalFileName($url, $saveName);

  12.        $cfg = [

  13.            'appKey' => 'xxx',

  14.            'appSecret' => 'xxx',

  15.            'appId' => 'xxx',

  16.        ];

  17.        $util = new BaiduVoiceUtil($cfg);

  18.        $code = exec("bash /www/silk-v3-decoder/converter.sh {$saveName} wav");

  19.        if ($code == 0) {

  20.            $result = $util->asr($convertName);

  21.            if ($result['err_no'] == 0) {

  22.                $voicesResult = json_encode($result['result'], JSON_UNESCAPED_UNICODE);

  23.                $voice->result = $voicesResult;

  24.                $voice->save();

  25.                @unlink($saveName);

  26.                @unlink($convertName);

  27.            }

  28.        }

  29.    }

  30.    ...

  31. }

task服务的处理逻辑也很清晰:接收需要识别的voiceid,查找记录,把语音文件下到本地某个tmp目录,调用shell转换格式,将转换后的格式调用baidu的语音接口进行识别,再将结果入库。

voice表结构如下:


如此,便完成了语音识别功能。

红包分配

应用场景:创建红包时

打开红包一般有两种分配方法,一种是使用创建时便分配好每一份的份额。一种是打开时再动态分配,这里采取的是第一种。

具体讨论可在知乎:https://www.zhihu.com/question/22625187 找到。

说实话,看完这个答案还是学到了一些东西的,如微信红包的架构实现,分配写法等等。

因为我们的应用没有微信的量级,自然不需要考虑太多(负载,并发等),产品的要求也只是说金额这方面要实现类微信红包的分配方法即可。因此,考虑到扩展及性能以及时间,分配写法我直接采用了 陈鹏 的答案里的写法,不过是变成了PHP的版本。并且搭配了redis 作为红包份额的存储及可能的并发问题处理方案。

先上代码(redpack/create):

  1. $redpack = $this->getCreatedRedPackByBody();

  2. // ... 业务逻辑代码略

  3. // 设置随机红包份额

  4. $this->setRedPackOpenOdds($redpack);

  5. protected function setRedPackOpenOdds($rp)

  6. {

  7.    $remainNum = $rp->num;

  8.    $remainMoney = $rp->fee;

  9.    $key = 'redpack:'.$rp->id;

  10.    $redis = yii::$app->redis;

  11.    while (!empty($remainNum)) {

  12.        $money = $this->getRandomMoney($remainNum, $remainMoney);

  13.        $redis->executeCommand('RPUSH', [$key, $money]);

  14.    }

  15.    $redis->executeCommand('expire', [$key, 259200]);

  16. }

  17. protected function getRandomMoney(&$remainNum, &$remainMoney)

  18. {

  19.    if ($remainNum == 1) {

  20.        $remainNum--;

  21.        return $remainMoney;

  22.    }

  23.    $randomNum = StringUtil::getRandom(6, 1);

  24.    $seed = $randomNum / 1000000;

  25.    $min = 1;

  26.    $max = $remainMoney / $remainNum * 2;

  27.    $money = $seed * $max;

  28.    $money = $money <= $min ? $min : ceil($money);

  29.    $remainNum--;

  30.    $remainMoney -= $money;

  31.    return $money;

  32. }

这部分代码逻辑也相对简单,主要就是:

将当前金额和份数传入函数( getRandomMoney),在计算出当次的随机金额后,将该金额写入redis的一个list (key=redpack:id),然后将总金额和总份数减去,一直减完为止。

有几点值得注意的地方:

1.原答案里的随机数生成法使用了 java.math.BigDecimal. 可php没有对应的函数,自带的随机数也不好用。这里用的自己写的随机数生成方法 (获取6位的随机数字,然后除以它们的位数,就得到类似于 0.608948的随机数) 2.每个红包的份额设置了一天的过期时间,这是为了实现红包过期的功能。

redis里的结果(单位为分):

10元分配15个

100元分配7个:

50元分配25个:

可以看到基本实现了随机分配,也兼顾了手气最佳的要求。

使用也简单,打开红包获取份额的时候,使用这个list左边一个个出栈就行了。

红包地图

应用场景:查看周围发布的红包

这个实现的关键之处就是周边的坐标算法。首先,前提条件是创建红包时要获取到经纬度坐标,这个交由前端实现,我们只记录即可。

然后在调用这个接口时,把用户当前的经纬度传过来。根据这个经纬度计算出周边范围,然后查找表中在这个周边范围的记录即可。

代码如下:

  1. /**

  2. *

  3. * @param double $lng 经度

  4. * @param double $lat 纬度

  5. * @param integer $radius 范围

  6. * @return array

  7. */

  8. public function run($lng, $lat, $radius = 500)

  9. {

  10.    $coordinates = $this->getAroundByCoordinates($lng, $lat, $radius);

  11.    $field = 'id,lat,lng';

  12.    $data = (new Query())

  13.            ->select($field)

  14.            ->from('{{app_redpack}}')

  15.            ->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]))

  16.            ->all();

  17.    return ResponseUtil::getOutputArrayByCodeAndData(Api::SUCCESS, $data);

  18. }

  19. /**

  20. * 地球的圆周是24901英里。

  21. * 24,901/360度 = 69.17 英里 / 度

  22. * @param double $longitude 经度

  23. * @param double $latitude 纬度

  24. * @param integer $raidus 范围。单位米。

  25. * @return array

  26. */

  27. public function getAroundByCoordinates($longitude, $latitude, $raidus)

  28. {

  29.    (double) $degree = (24901 * 1609) / 360.0;

  30.    (double) $dpmLat = 1 / $degree;

  31.    (double) $radiusLat = $dpmLat * $raidus;

  32.    (double) $minLat = $latitude - $radiusLat;

  33.    (double) $maxLat = $latitude + $radiusLat;

  34.    (double) $mpdLng = $degree * cos($latitude * (pi() / 180));

  35.    (double) $dpmLng = 1 / $mpdLng;

  36.    (double) $radiusLng = $dpmLng * $raidus;

  37.    (double) $minLng = $longitude - $radiusLng;

  38.    (double) $maxLng = $longitude + $radiusLng;

  39.    return [$minLat, $minLng, $maxLat, $maxLng];

  40. }

关键就是getAroundByCoordinates 这个算法,它根据输入的经纬度及范围大小,计算出左上,左下,右上,右下四个角的坐标,在地图上标出来的话就是 一个长方形的范围。

有兴趣的可以根据 http://lbs.qq.com/tool/getpoint/ 这个工具,随意点取一个坐标,根据以上的方法算出四个角,看看是不是刚好是$raidus指定的范围。

需要说明的是这个方法不是我写的,但是我实在不记得出处在哪了。我只是记得把java的实现方法改成了php。对原作者说声抱歉


  • 本文已收录于以下专栏:

芝麻说说口令红包小程序软件

芝麻说说口令红包小程序软件《苏134-1619-5045》芝麻说说口令红包小程序软件,芝麻说说模式小程序口令红包系统。   小程序未来带来的颠覆性还会更多,因为它不再是是一个简简单单的通讯工...
  • swl1213
  • swl1213
  • 2018年01月06日 18:14
  • 447

微信红包新玩法:利是说语音口令红包

原来红包还有这种玩法,你知道吗?
  • applepai666
  • applepai666
  • 2017年10月24日 16:02
  • 10099

QQ抢红包插件实现

作为一个有女朋友的人,之前跟那些20年的单身狗抢红包是屡战屡败。再加上最近群里面发红包发的厉害,又想到快要过年了,到时候还不知道群里要发好多红包,所以我将之前在网上宕的一份微信抢红包的代码修改了一下,...
  • qq_26420489
  • qq_26420489
  • 2016年04月01日 14:50
  • 1626

怎么抢支付宝口令红包口令 支付宝抢红包的吱口令红包

怎么抢支付宝口令红包口令  支付宝抢红包的吱口令红包 支付宝再追10亿红包,最多99元!今天你领红包了吗?没有的话,复制下面活动链接; 长按复制此支付宝红包口令 :5eaRyc32Vj  打开支...
  • qq_35171556
  • qq_35171556
  • 2017年12月24日 14:03
  • 59

php实现发红包程序

php发红包程序的实现 前言: 使用PHP发红包,当我们输入红包数量和总金额后,PHP会根据这两个值进行随机分配每个金额,保证每个人都能领取到一个红包,每个红包金额不等,就是要求红包金额要有差异,所有...
  • v_wanglei
  • v_wanglei
  • 2016年06月25日 15:12
  • 906

今日推荐:【包你说】红包怎么玩,由你说了算!

自从微信推出的红包功能后,抢红包就成了各个微信群里乐此不疲的活动,微信大大不定时的发布一些新的红包玩法:面对面红包、ar红包、口令红包、黄金红包……层出不穷的玩法让群友们欲罢不能呀,今天小编带你领略新...
  • qq_38530880
  • qq_38530880
  • 2017年07月27日 18:18
  • 63428

固定总金额,红包数量,单红包金额上限,单红包金额下限拆分随机红包

public static void main(String[] args) { //单个红包下限 Long minAmount=150L; //单个红...
  • shanxiuwei
  • shanxiuwei
  • 2017年04月17日 10:42
  • 312

微信抢红包插件项目

前言:转载请注明出处:/hejjunlin/article/details/52116904 微信抢红包插件,红包精录源码,AccessibilitySer...
  • hejjunlin
  • hejjunlin
  • 2016年08月04日 10:43
  • 2297

网易云信红包功能接入指南(iOS)

网易云信的IM程序基础上,接入云叮当红包SDK(IOS)
  • yundinddang
  • yundinddang
  • 2017年05月11日 17:55
  • 429

甲骨文口令红包小程序源码开发

微信小程序催生了很多火爆的商业模式,口令红包小程序就是其中一种。甲骨文小程序认为,口令红包小程序开发成本低、推广简单、适用人群广,可以让企业营销增加更多趣味性,让分享变得更有价值。  一、用户为...
  • h_q_f666
  • h_q_f666
  • 2017年10月30日 14:11
  • 714
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:如何做一个小程序口令红包功能
举报原因:
原因补充:

(最多只允许输入30个字)