PK创意闹新春,我正在参加「春节创意投稿大赛」,详情请看:春节创意投稿大赛
效果
| 体验 | 发送页面 | 领取页面 | 拜年姿势识别页面 |
|---|---|---|---|
介绍
项目主要分为三部分 - 小程序 发红包、领红包、姿势采集 - 后端 登陆、发红包、支付、领红包、发钱、验证姿势是否正确 - 人体识别 采集到图片时识别人体关键点
1.小程序
- 使用 uni-app 开发
- ui使用 uview
- 目前只兼容 微信小程序
- 一共3个页面
2.后端
- 使用 java 开发
- 框架主要是要 springboot + mybatis-plus + IJPay-WxPay + redisson
3.人体识别
- 使用 python 开发
- 框架主要是要 Django + PaddlePaddle
一、发红包
- 发送人 默认微信获取的昵称,可以自定义,方便不同人写不同的名
- 领取方式目前分为3个
- 打开即可:点开红包直接领取
- 写祝福语:需填写完祝福语,就可以打开红包
- 手势拜年:需做出拜年动作,做对后即可领取
- 思路
- 小程序填写红包信息,请求服务端
- 服务端保存红包,调用微信支付生成微信支付订单,返回给小程序
- 小程序拿到微信支付所需的参数,唤起微信支付
- 小程序支付成功后,询问服务端结果,确定支付成功后,弹出成功消息
- 代码实现
- 页面 (用的ui框架很简单)
<view style="margin: 20rpx;"> <u--form labelPosition="left" labelWidth="80" :model="from_data"> <u-form-item label="发送人"> <u--input v-model="from_data.nickName" border="none" maxlength="20" color="#FFFFFF"> </u--input> </u-form-item> <u-form-item label="压岁钱个数"> <u--input v-model="from_data.num" border="none" type="number" maxlength="3" color="#FFFFFF"> </u--input> </u-form-item> <u-form-item label="压岁钱金额"> <u--input v-model="from_data.amount" border="none" type="digit" maxlength="7" color="#FFFFFF"> </u--input> </u-form-item> <u-form-item label="压岁钱类型"> <u-radio-group v-model="from_data.redPacketType" placement="row"> <u-radio :customStyle="{marginRight: '16rpx'}" v-for="(item, index) in redPacket_type" :key="index" :label="item.name" :name="item.name" labelColor="#FFFFFF"> </u-radio> </u-radio-group> </u-form-item> <u-form-item label="领取方式"> <u-radio-group v-model="from_data.receivingMethod" placement="row"> <u-radio :customStyle="{marginRight: '16rpx'}" v-for="(item, index) in receiving_method" :key="index" :label="item.name" :name="item.name" :disabled="item.disabled" labelColor="#FFFFFF"> </u-radio> </u-radio-group> </u-form-item> <u-form-item label="留言祝福"> <u--input v-model="from_data.redPacketBlessing" border="none" maxlength="30" color="#FFFFFF"> </u--input> </u-form-item> </u--form> <u-alert title="需支付 3% 手续费 包含(微信支付和运营费用)" type="error" effect="dark" closable></u-alert> <u-button type="primary" :loading="pay_loading_show" loadingText="支付中..." text="支付" throttleTime="1000" customStyle="margin-top: 10rpx" @click="pay"> </u-button> </view>- js(发红包、支付)
pay() { //校验 表单 let num = this.from_data.num let fee = this.from_data.amount * 100 let redPacketType = this.from_data.redPacketType if (num < 1) { uni.showToast({ title: '最少发1份压岁钱' }) return } else if (num > 500) { uni.showToast({ title: '最多发500份压岁钱' }) return } if (redPacketType == '拼手气') { if (fee / num < 30) { uni.showToast({ title: '单人最少0.3元' }) return } else if (fee / num > 10000) { uni.showToast({ title: '单人最多100元' }) return } } else if (redPacketType == '平分') { if (fee < 30) { uni.showToast({ title: '单人最少0.3元' }) return } else if (fee > 10000) { uni.showToast({ title: '单人最多100元' }) return } } // 表单 let that = this this.pay_loading_show = true let from = { nickName: this.from_data.nickName, num: this.from_data.num, amount: this.from_data.amount, redPacketBlessing: this.from_data.redPacketBlessing } if (this.from_data.redPacketType == '拼手气') { from.redPacketType = 1 } else if (this.from_data.redPacketType == '平分') { from.redPacketType = 2 } if (this.from_data.receivingMethod == '打开即可') { from.receivingMethod = 1 } else if (this.from_data.receivingMethod == '写祝福语') { from.receivingMethod = 2 } else if (this.from_data.receivingMethod == '手势拜年') { from.receivingMethod = 3 } // 生成红包订单 uni.$u.http.post('/redPacket/send', from).then(res => { console.log(JSON.stringify(res)) that.currentRedPacketId = res.redPacketId //res 是生成订单后 返回的结果 用于支付的参数 // 返回唤起微信支付 wx.requestPayment({ timeStamp: res.timeStamp, nonceStr: res.nonceStr, package: res.package, signType: res.signType, paySign: res.paySign, success(res) { console.log('支付成功') that.currentRedPacket = that.from_data //支付成功后 查询支付结果 that.queryPayRes(1) }, fail(res) { console.log('支付失败') that.pay_loading_show = false that.refreshRedPacket() uni.showToast({ title: '支付失败' }) } }) }) }, // 查询支付结果 // 这里是轮训去查询,查询30次没500ms查一下 queryPayRes(count) { if (count == null) { count = 1 } uni.$u.http.post('/redPacket/queryPay', { redPacketId: this.currentRedPacketId }).then(res => { console.log('查询支付结果:' + JSON.stringify(res)) console.log('查询支付结果:' + res) if (res == true) { //支付成功后取消 loading ,弹出支付成功 this.pay_show = true this.pay_loading_show = false this.refreshRedPacket() } else { if (count < 30) { setTimeout(() => { this.queryPayRes(++count) }, 500) } else { this.pay_fail_show = true this.pay_loading_show = false this.refreshRedPacket() } } }) },- java 后端发红包接口
@Override public Result<?> send(UserBo user, RedPacketVo redPacketVo) { //校验参数 double fee = NumberUtil.mul(redPacketVo.getAmount(), new Double(100.00)); if (redPacketVo.getNum() > fee) { return Result.parameterError("每人最少1分钱"); } Integer redPacketType = redPacketVo.getRedPacketType(); Integer receivingMethod = redPacketVo.getReceivingMethod(); if (redPacketType == null || receivingMethod == null) { return Result.parameterError(); } //生成红包 RedPacket redPacket = new RedPacket() .setNum(redPacketVo.getNum()) .setRedPacketBlessing(redPacketVo.getRedPacketBlessing()) .setRedPacketType(redPacketType) .setReceivingMethod(receivingMethod) .setNickName(redPacketVo.getNickName()) .setUserId(user.getUserId()) .setStatus(0) .setOutTradeNo(WxPayKit.generateStr()); if (redPacketType.equals(1)) { 拼手机红包,需算出每个红包的金额 BigDecimal roundTotalFee = NumberUtil.round(fee, 0); redPacket.setTotalFee(roundTotalFee.intValue()); redPacket.setPayTotalFee(NumberUtil.mul(roundTotalFee, 1.03).intValue()); //红包算法 (可以有 公平版、也可以有手速版,所以不一定随先抢就是大红包,哈哈哈哈) List<Integer> redPacketFeeList = doubleMeanMethod(redPacket.getTotalFee(), redPacketVo.getNum()); //每个红包的金额都存起来 redPacket.setRedPacketFeeList(redPacketFeeList); } else if (redPacketType.equals(2)) { //平分红包 ,需要计算一下总金额 double totalFee = NumberUtil.mul(fee, redPacketVo.getNum().longValue()); BigDecimal roundTotalFee = NumberUtil.round(totalFee, 0); redPacket.setTotalFee(roundTotalFee.intValue()); redPacket.setPayTotalFee(NumberUtil.mul(roundTotalFee, 1.03).intValue()); //每个红包的金额都存起来 List<Integer> redPacketFeeList = new ArrayList<>(); int roundFee = NumberUtil.round(fee, 0).intValue(); for (int i = 0; i < redPacketVo.getNum(); i++) { redPacketFeeList.add(roundFee); } redPacket.setRedPacketFeeList(redPacketFeeList); } //存下红包 boolean save = save(redPacket); if (save) { //调用微信支付接口生产微信支付订单 Map<String, String> data = weChatPayService.miniAppPay(user, redPacket); data.put("redPacketId", redPacket.getRedPacketId().toString()); //返回微信支付需要的支付参数 return Result.success(data); } return Result.exceptionError("发红包失败"); } /** * 拼手气红包 * 二倍均值+最小金额 算法(公平版) * * @param totalFee 红包总金额(分) * @param size 领取人数 */ public static List<Integer> doubleMeanMethod(int totalFee, int size) { List<Integer> result = new ArrayList<>(); if (totalFee < 0 && size < 1) { return Collections.emptyList(); } //每个红包最小金额 int minFee = 30; int amount, sum = 0; int remainingNumber = size; int i = 1; while (remainingNumber > 1) { int maxFee = Math.min(2 * (totalFee / remainingNumber), totalFee - ((size - i + 1) * minFee) + minFee); amount = RandomUtils.nextInt(minFee, maxFee); sum += amount; System.out.println("第" + i + "个人领取的红包金额为:" + amount); totalFee -= amount; remainingNumber--; result.add(amount); i++; } result.add(totalFee); System.out.println("第" + i + "个人领取的红包金额为:" + totalFee); sum += totalFee; System.out.println("验证发出的红包总金额为:" + sum); return result; }