最近公司有需求搞个抽奖活动,看了好多文档,都是到差不差的,记录一下,将就看,肯定还有改进的地方
首先还是公众号授权信息,获得授权信息后,拿到openid,传给easywechat的获取单个用户信息方法,查询当前用户是否关注公众,若为关注,返回给前端,前端判断显示二维码还是大转盘,页面就不说了,展示效果自己画吧
大转盘的页面百度网盘 提取码:yoza
点击开始后判断当前活动时间是否结束,当前抽奖次数是否已经抽完毕等等提示
然后获取当前抽奖的活动奖品,获取中奖号
//获取对应的概率率数组
$rateArr = array_column($prmodel, 'probability');
// 测试中将几率
// foreach ($prmodel as $k => $v) {
// $prmodel[$k]['中奖次数'] = 0;
// $prmodel[$k]['概率'] = strval($v['probability']) . '%';
// unset($prmodel[$k]['probability']);
// }
// for ($i = 0; $i < 10000; $i++) {
// $result = $this->getRandPrize($rateArr);
// $prmodel[$result]['中奖次数']++;
// }
// dd($prmodel);
// 返回中奖号
$result = $this->getRandPrize($rateArr);
/**
* 返回转盘抽奖结果
* @param array $proArr 概率数组(值为概率)
* @return int|string 返回对应数组的下标
*/
private function getRandPrize($proArr)
{
$result = '';
//概率数组的总概率精度
$proSum = array_sum($proArr);
//概率数组循环
foreach ($proArr as $key => $proCur) {
$randNum = mt_rand(1, $proSum);
if ($randNum <= $proCur) {
$result = $key;
break;
} else {
$proSum -= $proCur;
}
}
unset ($proArr);
return $result;
}
再根据当前返回的下标去获得对应的数组数据,如果奖品数量不为0,就减去当前奖品数量,再次查询当前奖品数量,若为0,将当前奖品的概率设置为0,并且把概率相加到免费的奖品上去。
如果不是免费的奖品,将生成二维码线下客服扫码核销,生成二维码用的扩展 phpqrcode,
二维码生成并且带logo
public function createQRcode($url,$qname,$filename)
{
Vendor('phpqrcode.phpqrcode');
$value = $url; //二维码内容
$errorCorrectionLevel = 'H'; //容错级别
$matrixPointSize = 6; //生成图片大小
$newqrcode = new \QRcode();
// 判断是否有这个文件夹 没有的话就创建一个
if(!is_dir("qrcode")){
// 创建文件夹
mkdir("qrcode");
}
//二维码图片保存路径
$pathname = ROOT_PATH . '/public/'.$filename.'/';
//生成二维码图片
$qrfilename = $filename.'/'.$qname. time() . rand(10000, 9999999) . '.png';
$newqrcode->png($value,$qrfilename , $errorCorrectionLevel, $matrixPointSize, 2);
$logo = ROOT_PATH.'public/assets/img/logo.png'; //准备好的logo图片
$QR = $qrfilename; //已经生成的原始二维码图
if (file_exists($logo)) {
$QR = imagecreatefromstring(file_get_contents($QR)); //目标图象连接资源。
$logo = imagecreatefromstring(file_get_contents($logo)); //源图象连接资源。
$QR_width = imagesx($QR); //二维码图片宽度
$QR_height = imagesy($QR); //二维码图片高度
$logo_width = imagesx($logo); //logo图片宽度
$logo_height = imagesy($logo); //logo图片高度
$logo_qr_width = $QR_width / 4; //组合之后logo的宽度(占二维码的1/5)
$scale = $logo_width/$logo_qr_width; //logo的宽度缩放比(本身宽度/组合后的宽度)
$logo_qr_height = $logo_height/$scale; //组合之后logo的高度
$from_width = ($QR_width - $logo_qr_width) / 2; //组合之后logo左上角所在坐标点
//重新组合图片并调整大小
/*
* imagecopyresampled() 将一幅图像(源图象)中的一块正方形区域拷贝到另一个图像中
*/
imagecopyresampled($QR, $logo, $from_width, $from_width, 0, 0, $logo_qr_width,$logo_qr_height, $logo_width, $logo_height);
}
//输出图片
imagepng($QR, $qrfilename);
imagedestroy($QR);
imagedestroy($logo);
// 返回浏览地址
// $qrappurl = $this->cdnurl("/".$filename,true);
// 框架内置 cdnurl
$qrappurl = cdnurl("/".$qrfilename, true);
return $qrappurl;
}
/**
* 获取上传资源的CDN的地址
* @param string $url 资源相对地址
* @param boolean $domain 是否显示域名 或者直接传入域名
* @return string
*/
function cdnurl($url, $domain = false)
{
$regex = "/^((?:[a-z]+:)?\/\/|data:image\/)(.*)/i";
$cdnurl = \think\Config::get('upload.cdnurl');
$url = preg_match($regex, $url) || ($cdnurl && stripos($url, $cdnurl) === 0) ? $url : $cdnurl . $url;
if ($domain && !preg_match($regex, $url)) {
$domain = is_bool($domain) ? request()->domain() : $domain;
$url = $domain . $url;
}
return $url;
}
生成后更新到当前中奖记录的二维码字段。
如果是中奖的红包1块,将直接发送红包1块到公众号,用户在公众号就能领取,也可以发送到余额,这里就只用到了发送红包,发送余额请参考 EasyWeChat 文档,红包发送需要到微信支付商户后台设置,每天每人领取的最多次数,每天每人可领取的最大金额,数据库也设置了红包的数量
/**
*订单号生成 18位
*/
public function CreateOrdernum()
{
$osn = date('Ymd') . substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 18);//订单号生成
return $osn;
}
/*
* 发送红包
* 现金红包发放后会以公众号消息的形式触达用户,不同情况下触达消息的形式会有差别,相关规则如下:
* 1.已关注公众号的用户,使用“防伪消息”触达;
* 2.未关注公众号的用户,使用“模板消息”触达。
* $lotteryname 活动名称
* $openid 用户openid
*/
public function sendredpack($lotteryname,$openid){
$payment = Factory::payment($this->config);
$redpack = $payment->redpack;
$redpackData = [
'mch_billno' => $this->CreateOrdernum(),// 商户订单号(每个订单号必须唯一。取值范围:0~9,a~z,A~Z)
'send_name' => '红包名称', // 红包发送者名称
're_openid' => $openid, // 接受红包的用户openid
'total_num' => 1, //固定为1,可不传,红包发放总人数
'total_amount' => 100, //单位为分,不小于100,每次1块钱
'wishing' => '恭喜您中奖啦', // 红包祝福语
'act_name' => $lotteryname,// 活动名称
'remark' => '测试备注',
'scene_id' => 'PRODUCT_2', // 发放红包使用场景,红包金额大于200或者小于1元时必传 PRODUCT_2:抽奖
];
// return_code 此字段是通信标识,非红包发放结果标识,红包发放是否成功需要查看result_code来判断
//
// sendNormal 普通个人红包
// sendGroup 裂变红包
$result = $redpack->sendNormal($redpackData);
// log::info('发送红包返回状态:' . print_r($result, true));
return $result;
}
发送红包是查看 “result_code” => “SUCCESS” 是否,错误的话返回 “result_code” => “FAIL” ,成功返回以下
成功
array:11 [
"return_code" => "SUCCESS"
"return_msg" => "发放成功"
"result_code" => "SUCCESS"
"err_code" => "SUCCESS"
"err_code_des" => "发放成功"
"mch_billno" => "订单号"
"mch_id" => "商户id"
"wxappid" => "公众id"
"re_openid" => "用户openid"
"total_amount" => " 金额100"
"send_listid" => "红包记录查询id"
]
失败
"return_code" => "SUCCESS"
"return_msg" => "每个红包的平均金额必须在1.00元到1.00元之间."
"result_code" => "FAIL"
"err_code" => "MONEY_LIMIT"
"err_code_des" => "每个红包的平均金额必须在1.00元到1.00元之间."
"mch_billno" => "订单号"
"mch_id" => "商户id"
"wxappid" => "公众id"
"re_openid" => "用户openid"
"total_amount" => "30" // 错误的金额
成功后生成红包发送记录,方便后续查看
前台客服扫码核销兑换奖品,微信扫一扫授权登录,授权回调判断是否为扫码客服,前台点击确认核销,核销当前奖品。
在我的奖品里面,点击兑换按钮,展示当前奖品的二维码,若状态不为可兑换奖品,直接展示结果,红包则展示已发送到公众号,以及发送红包时间。
大转盘的文字是没有换行的,只能一行展示,也可以换成图片,下面是换行
/**
* canvas处理文字超长自动换行
* @type {number}
*/
ctx.lineWidth=1;
var lineWidth = 0;
var canvasWidth = canvas.width;//计算canvas的宽度
var initHeight=15;//绘制字体距离canvas顶部初始的高度
var lastSubStrIndex= 0; //每次开始截取的字符串的索引
lineWidth+=ctx.measureText(info[i]).width;
if(lineWidth>(canvasWidth/4)){
ctx.fillText(info[i].substring(lastSubStrIndex,4),-30,-125,60);//绘制截取部分
initHeight+=20;//20为字体的高度
lineWidth=0;
lastSubStrIndex=i+5;
if(i <= info[i].length-1){//绘制剩余部分
ctx.fillText(info[i].substring(4,i+6),-30,-105,55);
}
}else{
ctx.fillText(info[i],-30,-125,60);
// ctx.fillText(info[i],80,-5,60);
}
页面效果,进度条使用的layui的,后台计算值后返回给前台就行
右下角的奖品点击开始后,会更新数据,可以获取缓存来更新
奖品页面
兑换二维码
有待升级