当前位置:首页 > PHP专题 > 正文

php实现连续签到领积分或者礼品

01-15 PHP专题 原创


PHP实现连续签到的功能在很多网站无处不见,有一些是实现心情签到,发表心情。有一些是实现连续签到领取积分或者礼品之类的,根据业务的需求改变而改变,今天要来给大家讲解的是如何实现签到领取积分,连续签到领取礼品之类的功能

如图所示,签到之前


签到之后的页面显示


这里给大家贴各种实现的核心代码,学习php主要是大家能借鉴,看懂思路,而不是各种复制黏贴哈!
需求的需要,本功能实现需要用到三张表:
签到奖励设置表

CREATE TABLE `le_sign_in` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '递增主键',
  `name` varchar(100) NOT NULL COMMENT '签到名称',
  `icon` varchar(100) NOT NULL COMMENT '签到图标',
  `type` tinyint(3) unsigned NOT NULL DEFAULT '2' COMMENT '1. 常规 2. 额外',
  `keep_sign_in` tinyint(3) unsigned NOT NULL COMMENT '保持签到',
  `reward_rule` varchar(255) NOT NULL COMMENT '签到奖励规则:[ {reward_type:奖励类型1.积分 2.红包 reward_min:奖励最小值,reward_max:奖励最大值}]',
  `reward_sort` tinyint(3) unsigned NOT NULL DEFAULT '100' COMMENT '奖励排序',
  `admin_id` int(10) unsigned NOT NULL COMMENT '管理员用户ID',
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  `deleted_at` datetime DEFAULT NULL COMMENT '是否删除',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 COMMENT='签到奖励设置表';
日志表
CREATE TABLE `le_sign_in_log` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(10) unsigned NOT NULL COMMENT '用户ID',
  `created_at` datetime DEFAULT NULL COMMENT '签到时间',
  PRIMARY KEY (`id`),
  KEY `u_user_and_time` (`user_id`,`created_at`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=245 DEFAULT CHARSET=utf8 COMMENT='用户签到日志表';
签到奖励日志表
CREATE TABLE `zmq_sign_in_reward_log` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'v',
  `sign_in_log_id` int(10) unsigned NOT NULL COMMENT 'sign_id_log表id',
  `sign_in_id` int(10) NOT NULL COMMENT '签到ID',
  `reward_type` tinyint(3) unsigned NOT NULL COMMENT '奖励类型(1.积分 2.红包 )',
  `reward_quantity` decimal(10,2) unsigned NOT NULL COMMENT '奖励数量',
  `is_get` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否收到, 0未收到 1已收到',
  `get_time` datetime DEFAULT NULL COMMENT '收到时间',
  `type` tinyint(4) NOT NULL COMMENT '对应sign_in表中的type=2',
  `max_sign_in` smallint(5) unsigned NOT NULL COMMENT '最大连续签到',
  `keep_sign_in` smallint(5) unsigned NOT NULL COMMENT '保持签到次数',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=224 DEFAULT CHARSET=utf8 COMMENT='签到奖励日志表';

前端模板文件,这里主要是用vue页面实现
<p id="sign_in" v-cloak>
    <p class="sign_in_box">
        <p class="sign_in_record">
            <p class="active_box">
                <p>连续签到</p>
                <p class="number">{{ keepSignIn }}<em>天</em></p>
            </p>
            <p class="slash_bg"></p>
            <p class="history_box">
                <p>最高连续记录</p>
                <p class="number">{{ maxSignIn }}<em>天</em></p>
            </p>
        </p>
        <p v-if="!signIn" class="sign_in_btn" @click="sign_in">签到</p>
        <p v-else class="sign_in_btn">已签到,获得{{ signReward }}</p>
        <a href="/mobile/signIn/log" class="sign_in_entrance">签到记录</a>
    </p>
    <p class="sign_in_hint" v-show="nextRewardDay">再坚持连续签到{{ nextRewardDay }}天,就可以获得额外奖励哦~</p>
    <p class="calendars_box">
        <p class="today_title">{{currentYear}}年{{currentMonth}}月{{currentDay}}日</p>
        <ul class="week_box">
            <li>日</li>
            <li>一</li>
            <li>二</li>
            <li>三</li>
            <li>四</li>
            <li>五</li>
            <li>六</li>
        </ul>
        <ul class="calendars_list">
            <li v-for="i in last_month"></li>
            <li v-for="(item,index) in signInData" @click="showRewardList(item)" :class="{'active':item.isActive == 1, 'today':index == currentDay-1}">
                <p v-if="item.is_get == 1" class="prize_price"><p>{{ item.reward_type_zh }}</p><p>{{ parseFloat(item.reward_quantity) }}</p></p>
                <p v-else >{{ index+1 }}</p>

                <!--有奖励并且没有领取则显示图片, 并且只有当天有额外奖励才显示-->
                <p v-if="item.has_reward == 1 && item.is_get == 0" class="signIn_prize"><img :src="item.icon"></p>

                <!-- 当天已签到,并且有奖励,还没有领取的则显示提示 -->
                <p v-if="item.isActive == 1 && item.has_reward == 1 && item.is_get == 0 && (index) == (currentDay-1)" class="item_hint"></p>
            </li>
        </ul>
    </p>
    <p class="signIn_banner" v-if="ad"><a :href="ad.link"><img :src="ad.pic"></a></p>
    <p v-show="signIn_pop" @click="signIn_pop = !signIn_pop" class="mark_bg2"></p>
    <p v-show="signIn_pop" class="signIn_pop">
        <p v-if="!continue_signIn" class="top_bg"></p>
        <p v-else class="top_bg2"></p>
        <p v-if="signIn_pop_style==1" class="pop_title">{{ reward }}</p>
        <p v-if="signIn_pop_style==2" class="pop_title">请选择你的奖励!</p>
        <p v-if="signIn_pop_style==3" class="pop_title">{{ reward }}</p>
        <p v-if="signIn_pop_style==1||signIn_pop_style==3" class="odd_goods" :class="{ 'active':signIn_pop_style == 3}"><img :src="`/static/mobile/images/new/sign_in_type_${signInRewrdType}.png`"></p>
        <ul v-if="signIn_pop_style==2" class="even_goods">
            <li v-for="(type, index) in currentReward.reward_type" @click="select_prize(index)" v-if="rewardNum >= index" :class="{'active':selectPrize == index}">
                <img :src="`/static/mobile/images/new/sign_in_type_${type}.png`">
                <p v-if="type==1">积分</p>
                <p v-if="type==2">现金红包</p>
            </li>
        </ul>
        <a @click="signIn_pop = !signIn_pop" v-if="signIn_pop_style==1||signIn_pop_style==3" class="close_pop" href="javascript:;">我知道了</a>
    </p>
</p>
vue实现的代码,主要是与php后端代码的交互,与如何渲染到模板
new Vue({
        el: '#sign_in',
        data: {
            ad:undefined, //广告
            signIn:false,//是否已经签到
            signInData:[],
            today:'',
            currentYear: 1970, // 年份
            currentMonth: 1, // 月份
            currentDay: 1, // 日期
            currentWeek: 1, // 星期
            last_month:'',//上个月剩余多少天
            signIn_pop:false,//签到弹窗
            continue_signIn:true,//是否持续签到
            signIn_pop_style:3,//1.2.3分别是1领取单个2领取两个3领取后
            selectPrize:-1,//两种奖品选择
            keepSignIn:0,//连续签到
            maxSignIn:0,//最大签到
            reward:"",      //弹出显示的奖励(用于签到和额外奖励)
            signReward:"",  //签到显示的奖励(仅用于签到)
            nextRewardDay:0,    // 下一次签到奖励
            rewardNum:1,        // 额外奖励可选数量
            currentReward:0,         // 现在额外奖励
            signInRewrdType:0,
        },
        created () {

            layer.open({type: 2, shadeClose: false})

            var that = this;

            this.$http.post('/mobile/signIn', {}, {emulateJSON:true}).then(function (res) {
                if (res.body.status == 1) {
                    var data = res.data.data
                    that.signInData = data.calendars;
                    that.maxSignIn = data.max_sign_in;
                    that.keepSignIn = data.keep_sign_in;
                    if (data.today_sign_in) {
                        that.signReward = that.format_sign_in(data.today_sign_in)
                        that.reward = that.format_reward(data.today_sign_in)
                        that.signIn = true
                    } else {
                        that.signIn = data.today_sign_in
                    }
                    layer.closeAll()
                    this.nextRewardTips()
                }
            })

            this.$http.get('/mobile/index/ads?ad_type=12').then(function(res) {
                if (res.body.status == 1) {
                    if (res.body.data[12]) this.ad = res.body.data[12][0]
                }
            })

            this.initData(null)
        },
        methods: {
            format_sign_in: function (reward) {
                if (!reward) return
                switch (parseInt(reward.reward_type)) {
                    case 1:
                        return ` ${reward.reward_quantity} 积分`;
                    case 2:
                        return ` ${reward.reward_quantity} 元`;
                   
                }
            },
            format_reward: function (reward) {

                if (!reward) return
                switch (parseInt(reward.reward_type)) {
                    case 1:
                        return `恭喜你,获得积分奖励 ${reward.reward_quantity} 积分`;
                    case 2:
                        return `恭喜你,获得红包奖励 ${reward.reward_quantity} 元`;
                  
                    default:
                        console.log(reward.reward_type)
                }
            },
            select_prize:function(e){
                layer.open({type: 2, shadeClose: false})
                this.selectPrize = e;

                var data = {log_id: this.currentReward.log_id, select: e}
                this.$http.post('/mobile/signIn/getReward', data, {emulateJSON:true}).then(function (res) {
                    layer.closeAll()
                    if (res.body.status == 1 && res.body.data) {
                        var data = res.body.data
                        this.reward = this.format_reward(data)
                        this.currentReward.is_get = 1
                        this.currentReward.reward_type = data.reward_type
                        this.currentReward.reward_type_zh = data.reward_type_zh
                        this.currentReward.reward_quantity = parseFloat(data.reward_quantity)
                        this.signInRewrdType = data.reward_type
                        this.showPopStyle(1)
                    } else {
                        layer.open({
                            content: res.body.message,
                            skin: 'msg',
                            time: 2 //2秒后自动关闭
                        });
                    }
                })

            },
            // 用户激活签到
            sign_in:function(){

                this.$http.post('/mobile/signIn/active', {}, {emulateJSON:true}).then(function (res) {
                    if (res.body.status == 1) {
                        var data = res.body.data

                        this.signReward = this.format_sign_in(data)
                        this.reward = this.format_reward(data)

                        this.signInRewrdType = data.reward_type
                        this.signIn=true;
                        this.signInData[this.currentDay-1].isActive = true;
                        this.signInData[this.currentDay-1].log_id = data.log_id
                        this.showPopStyle(1)

                        if (this.keepSignIn == this.maxSignIn) {
                            this.maxSignIn++
                        }
                        this.keepSignIn++

                        this.nextRewardTips()
                    }
                })
            },
            // 下一次签到奖励
            nextRewardTips: function () {
                for (var index in this.signInData) {
                    if (index >= this.currentDay) {
                        if (this.signInData[index].icon) {
                            this.nextRewardDay = index - this.currentDay + 1
                            break;
                        }
                    }
                }

            },
            //格式化日期
            formatDate: function (year, month, day) {
                const y = year
                let m = month
                if (m < 10) m = `0${m}`
                let d = day
                if (d < 10) d = `0${d}`
                return `${y}-${m}-${d}`
            },
            initData: function (cur) {
                let date = ''
                if (cur) {
                    date = new Date(cur)
                } else {
                    date = new Date()
                }
                this.currentDay = date.getDate() // 今日日期 几号
                this.currentYear = date.getFullYear() // 当前年份
                this.currentMonth = date.getMonth() + 1 // 当前月份
                this.currentWeek = date.getDay() // 1...6,0   // 今天是星期几

                //当前月的第一天是星期几
                date.setDate(1);
                this.firstWeek = date.getDay();
                this.last_month=this.firstWeek;
            },
            // 显示额外奖励列表
            showRewardList: function (item) {
                if (item.isActive == 1 && item.has_reward == 1 && item.is_get == 0) {
                    this.rewardNum = item.reward_num;
                    this.currentReward = item

                    // 额外奖励只有一个,直接领取
                    if (this.rewardNum == 1) {
                        this.select_prize(0)
                    } else {
                        this.showPopStyle(2)
                    }
                }

                if (item.is_get) {
                    this.reward = this.format_reward(item)
                    this.signInRewrdType = item.reward_type
                    this.showPopStyle(1)
                }
            },
            // 显示弹出框类型
            showPopStyle: function (i) {
                this.signIn_pop_style = i
                this.signIn_pop=true;
            }
        },
        mounted: function () {

        },
        computed: {
            rewardNumZh: function () {
                if (this.rewardNum == 1) {
                    return "一";
                }
                if (this.rewardNum == 2) {
                    return "二";
                }
            }
        }
    })
前端的代码基本就是那样,接下来看看后端实现的核心代码
用户签到,首先要获取签到的日历,处理过去和未来可能签到的奖励
/**
 * 获取签到日历
 * @param $uid int 用户ID
 * @param $containPast bool 是否包含过去签到奖励
 * @return array
 */
public function getCalendars($uid, $containPast=true)
{
	$signIns = $this->fetchPaginate(['return_model'=>1]);

	$calendars = $this->getCalendarsArray();

	// 处理过去已获得的签到奖励
	$past = function () use (&$calendars, &$uid) {
		$thisMonths = $this->log->fetchThisMonths($uid);
		$thisMonths->load(['extraReward.signIn'=>ToolModel::withSelect(['id','icon', 'reward_rule'])]);

		foreach ($thisMonths as $item) {
			$day = $item->day-1;
			$calendars[$day]['isActive'] = 1;

			if (isset($item->extraReward)) {
				$calendars[$day]['icon'] = $item->extraReward->signIn->icon;
				$calendars[$day]['has_reward'] = 1;
				$calendars[$day]['log_id'] = $item->id;
				$calendars[$day]['is_get'] = $item->extraReward->is_get;

				if ($item->extraReward->is_get == 0) {
					$calendars[$day]['reward_num'] = count($item->extraReward->signIn->reward_rule);
					$calendars[$day]['reward_type'] = array_column($item->extraReward->signIn->reward_rule, 'reward_type');
				} else {
					$calendars[$day]['reward_quantity'] = $item->extraReward->reward_quantity;
					$calendars[$day]['reward_type'] =$item->extraReward->reward_type;
					$calendars[$day]['reward_type_zh'] = $item->extraReward->reward_type_zh;
				}
			}
		}
	};

	// 处理未来可能有的签到奖励
	$future = function () use (&$calendars, &$signIns, &$uid) {
		if (empty($signIns))return;
		$user = $this->userSignIn->findByUserId($uid);
		$signInLen = count($signIns);
		$monthNum = count($calendars);
		$toDay = (date('d')-1);

		// 是否已签到,如果未签到则-1
		if ($this->isActive($uid)) {
			$start = $toDay - $user->keep_sign_in;
		} else {
			$start = $toDay - $user->keep_sign_in-1;
		}

		while ($start < $monthNum) {
			for ($i=0; $i<$signInLen; $i++) {
				$start += $signIns[$i]->keep_sign_in;
				if ($start >= $monthNum) {
					return;
				}
				if (0 > $start) {
					continue;
				}
				$calendars[$start]['icon'] = $signIns[$i]['icon'];
				$calendars[$start]['has_reward'] = 1;
				$calendars[$start]['reward_num'] = count($signIns[$i]->reward_rule);
				if ($calendars[$start]['is_get'] == 0) {
					$calendars[$start]['reward_type'] = array_column($signIns[$i]->reward_rule, 'reward_type');
				}


				// 设置今天的签到奖励
				if ($start == $toDay) {
					$this->toDayExtraSignReward = $signIns[$i];
				}
			}
		}
	};

	if ($containPast) $past();

	$future();

	return $calendars;
}
签到成功后,获取奖励,
/**
 * 获取今天的签到奖励
 * @return array
 */
public function getToDayReward($uid)
{
	$log = $this->log->findByToDay($uid);

	if (empty($log)) return false;

	$log->load(['defaultReward']);

	return ['reward_quantity'=> $log->defaultReward->reward_quantity,
		'log_id'    => $log->id,
		'reward_type'=>$log->defaultReward->reward_type,
		'reward_type_zh'=>$log->defaultReward->reward_type_zh,
	];
}
添加用户奖励
/**
 * 添加用户签到奖励
 * @param UserSignBaseModel $userSign 用户签到
 * @param SignInLogBaseModel $signInLog 用户签到日志
 * @param SignBaseModel $defaultReward 用户签到默认奖励
 * @param SignBaseModel $extraReward 用户签到额外奖励
 */
public function store($userSign, $signInLog, $defaultReward, $extraReward = null)
{
	$data = [];

	// 默认奖品立刻发放
	if (isset($defaultReward)) {
		foreach ($defaultReward->reward_rule as $rule) {
			$quantity = $this->getRewardQuantity($rule);

			$data[] = [
				'sign_in_log_id' => $signInLog->id,
				'sign_in_id'    => $defaultReward->id,
				'reward_type'   => $rule['reward_type'],
				'reward_quantity' => $quantity,
				'is_get'        => 1,
				'type'          => 1,
				'get_time'      =>  date('Y-m-d H:i:s'),
				'max_sign_in'   => $userSign->max_sign_in,
				'keep_sign_in'  => $userSign->keep_sign_in,
			];

			$this->sendReward($signInLog->user_id, $rule['reward_type'], $quantity,
				['signInLog'=>$signInLog]);
		}
	}

	// 额外签到奖品
	if (isset($extraReward)) {

		$data[] = [
			'sign_in_log_id' => $signInLog->id,
			'sign_in_id'    => $extraReward->id,
			'reward_type'   => 0,
			'reward_quantity' => 0,
			'is_get'        => 0,
			'type'          => 2,
			'get_time'      => null,
			'max_sign_in'   => $userSign->max_sign_in,
			'keep_sign_in'  => $userSign->keep_sign_in,
		];
	}

	SignInRewardLogBaseModel::insert($data);
}
获取额外的奖励以及发放奖励,比如今天你是连续签到的7天的,那你除了获取到积分外,还可以获取到系统设置的红包奖励等。
/**
 * 发放签到奖励
 * @param $uid int 用户ID
 * @param $type int 类型
 * @param $quantity float 数量
 * @param $extra array 额外参数
 */
public function sendReward($uid, $type, $quantity, $extra=[])
{

	$related_id = empty($extra['signInLog']) ? 0 : $extra['signInLog']->id;
	// 奖励类型(1.积分 2.红包 3.桶装水 4.5100)
	switch ($type) {
		case 1:
			$params = [
				'user_id' => $uid,
				'change_quantity' => $quantity,
				'related_type' => 25,
				'related_id'   => $related_id,
				'remark'       => '签到奖励'
			];
			Recharge::sendOf("PointsRecharge", $params);
			break;
		case 2:
			$openid = WeixinUserBaseModel::where('user_id', $uid)
				->where('weixin_config_id', config('common.weixin_config_id.zmqcenter'))->first();
			if (empty($openid)) {
				$id = config('common.weixin_config_id.zmqcenter');
				throw new Exception("无法找OPENID,user_id={$$uid},weixin_config_id={$id}");
			}
			$params = [
				'weixin_config_id' => config('common.weixin_config_id.zmqcenter'),
				'amount' => $quantity,
				'openid' => $openid->wxid,
				'desc'  => '签到奖励红包:'.$quantity.'元',
				'trade_no'=> 'qiandao'.$related_id.substr(uniqid(time()), 0, 8),
			];
			Recharge::sendOf("RedEnvelopeRecharge", $params);
			break;
		case 3:
			$params = [
				'user_id' => $uid,
				'goods_id' => config('goods.goods_water_id'),
				'change_quantity' => $quantity,
				'related_type' => 101,
				'related_id'   => $related_id,
			];
			Recharge::sendOf("AccumulativeWaterReward", $params);
			break;
		case 4:
			$params = [
				'user_id' => $uid,
				'goods_id' => config('goods.goods_5100_water_id'),
				'change_quantity' => $quantity,
				'related_type' => 101,
				'related_id'   => $related_id,
			];
			Recharge::sendOf("AccumulativeWaterReward", $params);
			break;
	}
}
至此,基本所有的核心代码都已贴出,根据需求来实现,主要还是要设计到数据表,这样才能做到事半功倍!

 

以上是本文的全部内容,希望对大家的学习有帮助,也希望大家多多支持 php自学中心 感谢阅读!