【项目实站】 php 实现抽奖代码详解【下篇】重构后的代码

接上篇。点击查看--》项目实站】 php 实现抽奖代码详解【中篇】 如何面对需求变更

上篇讲了如何设计代码,封装需求, 接下来就是代码实现了,如下:

class DemoLottery{

    public function lottery($lottery_id , $uid){

        //一些防并发的代码,略。。。。
        try {
            //取用户
            $trans = Yii::app()->db->beginTransaction();
            $sql = "select * from demo_user where id = $uid for update";  //对用户数据加行锁
            $userRow = Yii::app()->db->createCommand($sql)->queryRow();

            //进行校验处理,用户是否可抽奖。
            $sql = "select * from demo_lottery where id = $lottery_id ";
            $lotteryRow = Yii::app()->db->createCommand($sql)->queryRow();   //得到抽奖活动数据
            //2.1 校验用户可否抽奖    list( $bool  ,  $msg) = $this->validate($lotteryRow , $userRow);   //进行校验。
          if( !$bool ) exit($msg );
            //2.2 使用抽奖机会。
            list($bool ,$msg)=  $this->useChance($lotteryRow , $userRow);  //使用掉用户的抽奖机会。
            if( !$bool ) exit($msg );

            //2.3 获得奖品数据
            $prizeRow = $this->getPrize( $lotteryRow , $userRow);

            //2.4 将奖品发放给客户。
            list($bool , $msg ) = $this->award($lotteryRow , $userRow , $prizeRow);
            if( !$bool ) exit($msg );return true; } 
            catch (Exception $e) { 
                    Yii::log($e->getMessage(), CLogger::LEVEL_INFO, "log_error"); $trans->rollback(); return false; 
              } } }
             //调用代码 $uid = 1; $lottery_id = 1; $lotteryObj = new DemoLottery(); $lotteryOjb->lottery($lottery_id , $uid);
首先处理校验问题,这里代码有点绕。但这是一种非常常用的写法,很多框架内的实现(例如yii 的validate ) 就是用的这种写法。 这种写法习惯了后,还有个好的副作用,就是以后看框架源码会比较顺畅一点点

 /**
     * 校验抽奖.
     * @param $lotteryRow  抽奖活动
     * @param $userRow
     */
    public  function validate($lotteryRow , $userRow){

        //将基础版本的校验移过来.
        if( !$lotteryRow )  exit( "活动不存在!" );
        $time = time();
        if( $time < $lotteryRow["start_time"] || $time > $lotteryRow["end_time"]) { return array(false , "活动未开始或已结束");}
        $sql = "select * from demo_user where id = ".$userRow["id"];
        $userRow = Yii::app()->db->createCommand($sql)->queryRow();
        if( $userRow["free_chance"] + $userRow["points"]/$lotteryRow["spend_point"] < 1) {   return array(false , "无抽奖机会");}


        //对json 里配置的需要校验的部分进行校验。
        $jsonParam = json_decode($lotteryRow["params"]);
        if(isset($jsonParam->validate)){
            foreach( $jsonParam->validate as $key =>  $options ):
                $methodName = "valid_".$key;
                $className = "lotteryValid".ucfirst($key);

                //如果存在内部方法,则调用内部方法进行校验.
                if( method_exists($this , $methodName)){  //如果有  $this->valid_perday_limit  则使用$this->valid_day_limit 进行校验。
                    list($bool , $msg ) =    $this->$methodName($lotteryRow , $userRow , $options);
                    if( !$bool)  exit($msg);
                }else if(@class_exists($className)){ //若无内联方法,则找到对应的校验类,调用对应的校验对象进行校验。   
                     $validObj = new $className;
                    /**@var $validObj abstractLotteryValidate */
                    $validObj->setLotteryRow($lotteryRow);
                    $validObj->setUserRow($userRow);
                    $validObj->setOptions($options);
                    list($bool , $msg ) =    $validObj->validate();
                    if( !$bool)  exit($msg);
                }

            endforeach;
        }
        return array(true , "success");
    }

这样,形成了一个约定,当你配置里面的validate 
有一个 perday_limit (每日限制)的设置的时候,
你可以在DemoLottery 中写一个 valid_perday_limit($xx)  方法来进行校验。
也可以 用 lotteryValidPerday_limit   对象来处理。 下面这段代码就是 map 的规则。
$methodName = "valid_".$key;
$className = "lotteryValid".ucfirst($key);

下面,一样取一个例子来实现。

    //校验当日的抽奖上限次数。  使用的内联方法 $this-> valid_perday_limit 进行校验。

    private  function valid_perday_limit( $lotteryRow , $userRow , $options){

        //特殊不受限制天数
        if( isset($options->except_days) && $options->except_days){
            $except_day_arr = explode("," , $options->except_days);
            if( in_array( date("Y-m-d") , $except_day_arr)) { return array(true , "success");}
        }

        $s_time = strtotime(date("Y-m-d"));  $e_time = strtotime(date("Y-m-d 23:59:59"));
        $sql = "select count(id) from demo_prize_log where uid = ".$userRow["id"]." && create_at>= $s_time && create_at <= $e_time";
        $count = Yii::app()->db->createCommand($sql)->queryScalar();
        if( $count > $options->limit ){
            return array(false , "单日次数超限!");
        } else {
            return array(true , "success");
        }
    }
//使用外部校验类校验方法。
//每周的星期六,日 ,限vip,5,6,7 可以抽奖.
class lotteryValidVip_level extends abstractLotteryValidate{
    public function validate(){

        $options = $this->options;

        $w = date("w"); //今天周几
        if(isset($options->week_num) && in_array($w , explode(",", $options->week_num))){

             $user_level = $this->userRow["vip_level"];
             if( ! in_array($user_level , explode("," , $options->limit))){
                    return array(false , "用户vip等级不符");
             }
        }
        return array(true , "success");
    }
}


//抽象校验类。
abstract class  abstractLotteryValidate {

    public $userRow = array();
    public $lotteryRow = array();
    public $options;

   public function setUserRow( $userRow) { $this->userRow = $userRow;}
   public function setLotteryRow( $lotteryRow) { $this->lotteryRow = $lotteryRow;}
   public function setOptions( $options) { $this->options = $options; }


    /**
     * @return array( bool ,msg)
     */
   abstract  public function validate();
}   //2.2 使用掉抽奖机会。
//校验完后。接下来是消耗掉用户的抽奖机会或积分。

    public function useChance($lotteryRow ,$userRow){  //
        $lottery_type = $userRow["free_chance"]>0?"free_chance":"points";  //用户使用哪种方式抽奖。
        //扣除掉用户的抽奖机会.
        if( $lottery_type == "free_chance"){
            $sql = "update demo_user set free_chance = free_chance - 1 where id = ".$userRow["id"];
            $bool = Yii::app()->db->createCommand($sql)->execute();  if( !$bool ){ throw new Exception("执行失败".$sql); }
        }else{
            $spend_point = $lotteryRow["spend_point"];
            $sql  = "update demo_user set points = points - $spend_point  where id = ".$userRow["id"];;
            $bool = Yii::app()->db->createCommand($sql)->execute();
            if( !$bool ){ throw new Exception("执行失败".$sql); }
        }
        return array( true ,"success");

    }

//后面的写法类似,暂不赘述。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值