小程序模板網(wǎng)

微信小程序ofo小黃車+thinkphp5.0打造全棧應(yīng)用

發(fā)布時間:2018-01-03 09:43 所屬欄目:小程序開發(fā)教程

ofo至今還沒有微信小程序(很費解),每次用ofo都得去支付寶,很不方便,我用微信用的比較多,無意間在簡書上面看到某人寫了一個關(guān)于ofo的小程序,鏈接如下:給ofo小黃車擼一個微信小程序,不過數(shù)據(jù)都是模擬的,沒有數(shù)據(jù)庫,沒有后臺,這對于一個PHP(拍黃片)攻城獅來說,是可忍孰不可忍呀,剛剛學(xué)完七月老師的課程,受益匪淺,剛好自己動手做一個,說動手就動手,let's do it;

先獻(xiàn)上一波效果圖吧:

體驗版頁面

支付頁面

計費頁面

開鎖頁面

用車頁面

開鎖頁面

充值頁面

個人中心頁面

我的錢包頁面

首頁頁面

ofo小程序的架構(gòu)體系:

小程序數(shù)據(jù)從服務(wù)器到前端交互總結(jié):

數(shù)據(jù)庫設(shè)計:

用戶表:

**user  | CREATE TABLE `user` (**
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`openid` varchar(50) NOT NULL COMMENT '用戶的唯一標(biāo)識',
`create_time` int(11) DEFAULT NULL,
`delete_time` int(11) DEFAULT NULL,
`balance` decimal(60,2) NOT NULL COMMENT '余額',
`guarantee` decimal(60,2) NOT NULL COMMENT '保證金',
`update_time` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 |

小黃車表:

**| bike  | CREATE TABLE `bike` (**
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`latitude` float(11,6) NOT NULL COMMENT '經(jīng)度',
`is_show` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0未使用 1使用',
`longitude` float(11,6) NOT NULL COMMENT '緯度',
`password` int(11) NOT NULL COMMENT '單車密碼',
`type` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0正常,1故障',
`create_time` int(11) NOT NULL,
`update_time` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 |

故障分類表:

**| trouble_cate | CREATE TABLE `trouble_cate` (**
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL COMMENT '故障名稱',
`create_time` int(11) DEFAULT NULL,
`update_time` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 |

故障記錄表:

**| trouble_record | CREATE TABLE `trouble_record` (**
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '用戶ID',
`bike_id` int(11) DEFAULT NULL COMMENT '單車ID',
`longitude` varchar(50) NOT NULL COMMENT '經(jīng)度',
`latitude` varchar(50) NOT NULL COMMENT '緯度',
`img` varchar(50) DEFAULT NULL COMMENT '上傳的圖片',
`remark` varchar(50) DEFAULT NULL COMMENT '備注',
`create_time` int(11) NOT NULL,
`update_time` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8 |

充值表:

**| charge | CREATE TABLE `charge` (**
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '用戶ID',
`price` decimal(60,2) NOT NULL COMMENT '費用',
`type` tinyint(1) NOT NULL DEFAULT '1' COMMENT '0為保證金 1為余額',
`create_time` int(11) NOT NULL,
`update_time` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8 |

騎行記錄表:

**| record | CREATE TABLE `record` (**
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`bike_id` int(11) NOT NULL COMMENT '單車ID',
`user_id` int(11) NOT NULL COMMENT '用戶ID',
`end_time` int(11) NOT NULL COMMENT '結(jié)束時間',
`start_time` int(11) NOT NULL COMMENT '開始時間',
`total_price` decimal(10,0) NOT NULL COMMENT '總價格',
`start_long` varchar(50) NOT NULL COMMENT '開始經(jīng)度',
`start_lati` varchar(50) NOT NULL COMMENT '開始緯度',
`end_long` varchar(50) NOT NULL COMMENT '結(jié)束經(jīng)度',
`end_lati` varchar(50) NOT NULL COMMENT '結(jié)束緯度',
`create_time` int(11) NOT NULL,
`update_time` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=47 DEFAULT CHARSET=utf8 |
核心知識體系:
1.thinkphp5.0相關(guān)的知識
  • TP5三大核心:路由、控制器、模型
  • 以O(shè)RM的方式查詢數(shù)據(jù)庫
  • 使用TP5驗證器Validate構(gòu)建整個驗證層
  • 開發(fā)環(huán)境和生產(chǎn)環(huán)境下不同的全局異常處理機(jī)制
  • TP5緩存的使用
  • 在TP5中使用數(shù)據(jù)庫事務(wù)
    2.微信小程序+微信支付
  • 微信小程序登錄狀態(tài)維護(hù)
  • 微信支付接入
  • Class和Module面向?qū)ο蟮乃季S構(gòu)建前端代碼
  • 體驗優(yōu)化
    3.API接口的設(shè)計
  • 采用RESTFul API風(fēng)格
  • (RESTFul API風(fēng)格可參考GitHub 開發(fā)者文檔)
  • 返回碼、URL語義、HTTP動詞、錯誤碼、異常返回
  • 使用Token令牌來構(gòu)建用戶授權(quán)體系
  • API版本控制(v1、v2)
ofo頁面邏輯和所需接口分析
1.首頁頁面邏輯與接口分析

根據(jù)效果圖,很明顯我們知道肯定需要一個獲取單車信息的接口,接口代碼如下:

/**
     * @return false|\PDOStatement|string|\think\Collection
     * @throws BikeException
     * 獲取單車的位置信息
     */
    public function getBicyclePosition() {
        $bikes = BikeModel::getBicyclePosition();
        if(!$bikes) {
           throw new BikeException();
        }
        return $bikes;
    }

立即用車按鈕分析,首先我們需要先判斷有沒有登錄,登錄我們使用的是token令牌(后面會在個人中心登錄按鈕講下如何生成token令牌,如何利用tp5的緩存,使token令牌有有效期),如果令牌存在,我們還得判斷令牌是否有效,否則重新登錄,如果驗證通過,我們還得判斷這個用戶是否已經(jīng)有押金,如果沒有押金,跳到充值頁面去充值,否則跳轉(zhuǎn)到用車頁面,根據(jù)分析,我們需要一個驗證token是否有效的接口,接口代碼如下,

/**
     * @return bool
     * @throws TokenException
     * 驗證token
     */
    public function verifyToken() {
        $token = Request::instance()->header('token');
        $var = Cache::get($token);
        if(!$var) {
            throw new TokenException([
                'msg'=>'token已經(jīng)過期',
                'errorCode'=>10002
            ]);
        }
        return true;
    }

我們還需要一個獲取用戶信息的接口,判斷是否有押金,接口代碼如下:

/**
     * @return null|static
     * @throws UserException
     * 獲取用戶的信息
     */
    public function getUserInfo(){
        $uid = Token::getCurrentUid();
        $user = UserModel::get($uid);
        if(!$user) {
            throw new UserException();
        }
        return $user;
    }

故障按鈕分析:同樣的我們需要驗證是否登錄,登錄是否過期,否則我們跳轉(zhuǎn)到登錄頁面。(注意:我們需要把用戶的初始位置,記錄到小程序的緩存中,因為騎行記錄表需要記錄用戶的初始位置)

2.登錄頁面邏輯和所需接口分析

關(guān)于使用token令牌的好處,請自行百度,首先我先用一張圖來說明微信小程序如何獲取token:

根據(jù)效果圖,我們需要獲取token令牌接口,接口代碼如下:

 /**
     * @param $code
     * @return array
     * 獲取token
     */
    public function getToken($code) {
        (new TokenGet())->goCheck();
        $user = new UserToken($code);
        $token = $user->get();
        return [
            'token'=>$token
        ];
    }

設(shè)置token的有效期,把token存儲在服務(wù)器端的緩存中,返回token,客戶端獲取到token,存儲到緩存中,雙向存儲token,以后每次訪問接口都攜帶token,更加安全,有效的防止有人偽造token獲取接口的信息

3.個人中心頁面邏輯和所需接口分析

根據(jù)效果圖,點擊我的錢包按鈕需要跳轉(zhuǎn)到我的錢包頁面,我們需要一個獲取用戶信息的接口,接口代碼如下:

/**
     * @return null|static
     * @throws UserException
     * 獲取用戶的信息
     */
    public function getUserInfo(){
        $uid = Token::getCurrentUid();
        $user = UserModel::get($uid);
        if(!$user) {
            throw new UserException();
        }
        return $user;
    }

退出登錄按鈕:我們需要刪除本地token,跳轉(zhuǎn)到登錄頁面

4.充值頁面邏輯和接口分析

根據(jù)效果圖:我們需要一個充值的接口,因為是個人開發(fā),沒有商戶號,所以微信支付就沒有做,不過其實微信支付也并不難,附上微信支付的流程:

商戶系統(tǒng)和微信支付系統(tǒng)主要交互說明:
步驟1:用戶在商戶APP中選擇商品,提交訂單,選擇微信支付。
步驟2:商戶后臺收到用戶支付單,調(diào)用微信支付統(tǒng)一下單接口。參見【[統(tǒng)一下單API](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1)】。
步驟3:統(tǒng)一下單接口返回正常的prepay_id,再按簽名規(guī)范重新生成簽名后,將數(shù)據(jù)傳輸給APP。參與簽名的字段名為appidpartnerid,prepayid,noncestr,timestamppackage。注意:package的值格式為Sign=WXPay
步驟4:商戶APP調(diào)起微信支付。api參見本章節(jié)【[app端開發(fā)步驟說明](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5)】
步驟5:商戶后臺接收支付通知。api參見【[支付結(jié)果通知API](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_7)】
步驟6:商戶后臺查詢支付結(jié)果。,api參見【[查詢訂單API](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_2)】

這個接口需要注意的是,從哪個頁面過來的,從首頁過來的,應(yīng)該就是押金充值,從我的錢包頁面和支付頁面過來的,就應(yīng)該是余額充值,根據(jù)form不同,我們數(shù)據(jù)庫充值記錄表里面的type就不同,type為1代表余額充值,type為1為押金充值,接口代碼如下:

/**
     * @param $guarantee
     * 充值
     */
    public function pay($from,$price) {
        $type = 1;
        if($from == 'index') {
            $type = 0;
        }else if($from == 'wallet' || $from == 'pay') {
            $type = 1;
        }
        $uid = Token::getCurrentUid();
        Db::startTrans();
        try{
            if($type == 1) {
                $user = UserModel::get($uid);

                $price = $price + $user->balance;
                $result = new UserModel();
                $res = $result->save(['balance'=>$price],['id'=>$uid]);
            }else {
                $res = UserModel::update(['guarantee'=>$price],['id'=>$uid]);
            }
            $rel = Charge::create([
                'price'=>$price,
                'type'=>$type,
                'user_id'=>$uid
            ]);
            if($rel && $res) {
                Db::commit();
            }
        }catch (Exception $e) {
            Db::rollback();
            throw new UserException([
                'msg'=>'充值失敗'
            ]);
        }
    }
5.立即用車頁面邏輯與接口分析

根據(jù)效果圖,我們需要一個獲取單車密碼的接口,根據(jù)用戶輸入的ID,獲取單車的信息,如果is_show為1,服務(wù)器拋出自定義的異常,單車正在被使用,type為1,單車被報修,出現(xiàn)故障,不能使用,單車如果不存在,拋出異常,單車不存在。獲取到單車的密碼后,攜帶密碼和單車號到結(jié)果頁面,接口代碼如下:

/**
     * @param $id
     * @return array|false|\PDOStatement|string|\think\Model
     * @throws BikeException
     * 根據(jù)單車的ID獲取單車的信息
     */
    public function getBikeByID($id) {
//        (new IsMustBePostiveInt())->goCheck();
        $bike = BikeModel::getBikeByID($id);
        if(!$bike) {
            throw new BikeException([
                'msg'=>'該車牌號不存在'
            ]);
        }
        if($bike['is_show'] == 1){
            throw new BikeException([
                'msg'=>'此單車正在被使用',
                'errorCode'=>10001
            ]);
        }
        if($bike['type'] == 1) {
            throw new BikeException([
                'msg'=>'此單車多次被報修,暫不可使用',
                'errorCode'=>10002
            ]);
        }
        return $bike;
    }
}
6.計時頁面邏輯和接口分析

根據(jù)效果圖:計時開始時,我們需要把單車的使用狀態(tài)改變,改變?yōu)檎谑褂脿顟B(tài),接口代碼如下:

/**
     * @param $id
     * 修改單車的使用狀態(tài)
     */
    public function updateBikeStatus($type = 0,$id) {
//        (new IsMustBePostiveInt())->goCheck();
        if($type == 0) {
            //鎖定單車,單車在被使用中
            $data = [
                'is_show'=>1
            ];
        }elseif ($type == 1) {
            //釋放單車,單車恢復(fù)使用
            $data = [
                'is_show'=>0
            ];
        }elseif ($type == 2) {
            //單車出現(xiàn)故障
            $data = [
                'type'=>1
            ];
        }elseif ($type == 3) {
            //單車恢復(fù)正常
            $data = [
                'type'=>0
            ];
        }

        $res = \app\api\model\Bike::update($data,['id'=>$id]);
        if($res) {
            return true;
        }else {
            echo false;
        }
    }
7.故障頁面邏輯和接口分析

根據(jù)效果圖,我們首先需要一個獲取故障分類名稱的接口,接口代碼如下:

 /**
     * @return false|\PDOStatement|string|\think\Collection
     * 獲取問題的分類信息
     */
    public function getTroubleCate() {
        $res = new \app\api\model\TroubleCate();
        $troubleCate = $res->select();
        return $troubleCate;
    }

然后提交的時候,我們需要一個記錄故障的接口,這個接口中,我們首先需要判斷,如果沒有選擇車牌損壞,則必須填寫車牌號,否則服務(wù)器返回自定義的異常,請輸入單車號,單車和故障很明顯是多對多的關(guān)系,我們在記錄的時候,還要寫到另外一張表中去,有記錄ID和分類ID組成的主鍵的表,同時我們根據(jù)單車的ID還得修改單車的狀態(tài),接口代碼如下:

public function recordTrouble($record) {
        //分為兩種情況,車牌損壞,車牌未損壞
       //如果有車牌號碼,先判斷單車是否存在,不存在,拋出異常,
        //如果存在,寫到trouble_record表,根據(jù)trouble_record
        //的id,還有trouble_id寫到bike_trouble表,多對多表,全部寫入成功之后,
        //修改bike表的type值,用到事務(wù),要么失敗,要么成功
        $bikeID = $record['inputValue']['num'];
        //2代表車牌被損壞,看不到車牌號碼
        if(!in_array(2,$record['checkboxValue'])) {
            if($bikeID) {
                $bike = new Bike();
                $bike->getBikeByID($bikeID);
            }else {
                throw new BikeException([
                    'msg'=>'請輸入單車編號',
                    'errorCode'=>10003
                ]);
            }
        }
        try {
            Db::startTrans();
            $address = $record['address'];
            $uid = \app\api\service\Token::getCurrentUid();
            $troubleRecord = new \app\api\model\TroubleRecord();
            $troubleRecord->user_id=$uid;
            $troubleRecord->bike_id=$bikeID;
            $troubleRecord->longitude=$address['start_long'];
            $troubleRecord->latitude=$address['start_lati'];
            $troubleRecord->img=json_encode($record['picUrls']);
            $troubleRecord->remark=$record['inputValue']['desc'];
            //更新故障記錄表troubleRecord
            $troubleRecord->save();

            $resID = $troubleRecord->id;

            $troublesID = $record['checkboxValue'];
            $newArr = array();
            foreach ($troublesID as $k=>$v) {
                $newArr[$k]['trouble_id'] = $v;
                $newArr[$k]['record_id'] = $resID;
            }
            $bikeTrouble = new BikeTrouble();
            //更新故障表bikeTrouble表
            $rel = $bikeTrouble->saveAll($newArr);

            if($bikeID) {
                //修改單車的狀態(tài),發(fā)送了故障
                $bike = new Bike();
                $bike->updateBikeStatus(2,$bikeID);
            }
            if($resID && $rel) {
                Db::commit();
            }
        }catch (Exception $e) {
            Db::rollback();
        }
    }
8.支付頁面的邏輯和接口分析

根據(jù)效果圖:我們需要一個記錄騎行的接口,這個接口中,這里有對多張表的操作,所以我們利用了tp的事務(wù)(注意:mysql數(shù)據(jù)引擎MyISAM不支持事務(wù)),提高數(shù)據(jù)庫數(shù)據(jù)的一致性,我們需要記錄用戶的開始地址,開始時間,結(jié)束地址,結(jié)束時間,總價格,用戶的id,單車的id等等,我們還需要修改用戶表的余額,同時修改小程序緩存的余額,關(guān)鍵點的是,我們還要再次獲取用戶的地址,及時修改單車的使用狀態(tài)和位置,便于其他用戶的使用,小黃車沒有GPS定位系統(tǒng),而是巧妙的利用了用戶的地址,這里我們看下小黃車的整個使用流程:

接口代碼如下:

 /**
     * @param $start_time
     * @param $bikeID
     * @param $end_time
     * @param $start_long
     * @param $start_lati
     * @param $end_long
     * @param $end_lati
     * @param $price
     * 用戶騎行后記錄到數(shù)據(jù)庫
     */
    public function record($start_time,$bikeID,$end_time,$start_long,$start_lati,$end_long,$end_lati,$price) {
        $uid = Token::getCurrentUid();
        $data = [
            'start_time'=>$start_time,
            'end_time'=>$end_time,
            'start_long'=>$start_long,
            'start_lati'=>$start_lati,
            'end_lati'=>$end_lati,
            'end_long'=>$end_long,
            'total_price'=>$price,
            'user_id'=>$uid,
            'bike_id'=>$bikeID
        ];
        Db::startTrans();
        try {
            //創(chuàng)建記錄
            $res = Record::create($data);

            //修改用戶的余額
            $user = new UserModel();
            $userInfo = $user->find($uid);
            $data = [
                'balance'=>$userInfo->balance-$price
            ];
            $rel = $user->save($data,['id'=>$uid]);

            //修改小黃車的狀態(tài)和位置
            $bikeData = [
                'is_show'=>'0',
                'latitude'=>$end_lati,
                'longitude'=>$end_long
            ];
            $rs = \app\api\model\Bike::update($bikeData,['id'=>$bikeID]);

            if($res && $rel && $rs) {
               echo 'success';
                Db::commit();
            }
        }catch (Exception $e) {
            Db::rollback();
        }
    }
結(jié)語

到這里,ofo小程序的制作就到了尾聲了。開篇我們簡單進(jìn)行了數(shù)據(jù)庫的設(shè)計,然后一個一個頁面從頁面分析,到完成接口設(shè)計,分別響應(yīng)著不同的業(yè)務(wù)邏輯,有的頁面與頁面之間有數(shù)據(jù)往來,我們就通過跳轉(zhuǎn)頁面?zhèn)鲄⒒蛟O(shè)置本地存儲來將它們建立起聯(lián)系,環(huán)環(huán)相扣,構(gòu)建起了整個小程序的基本功能,使原本的ofo小程序有了靈魂。
首先感謝慕課網(wǎng)和慕課網(wǎng)的講師七月老師,微信小程序商城構(gòu)建全棧應(yīng)用這門課程對我一個還沒畢業(yè),還沒有什么工作經(jīng)驗的小白來說影響很大,改變了我對傳統(tǒng)互聯(lián)網(wǎng)的看法,前后端分離,使分工更加明確,后端工程師只要專注于數(shù)據(jù)和業(yè)務(wù),這個項目做完,使我對前后端分離理解深刻,注意代碼的復(fù)用性,實踐才是王道,這個項目采用了tp5框架,自定義了全局異常類,自定義驗證器,加深了我對AOP思想的理解,使用restful API設(shè)計接口,更加符合規(guī)范。
源碼在我的github主頁上面,需要的請移步下載github鏈接,如果喜歡,請給一個start,謝謝


作者: 微末凡塵卻心向** 
鏈接:http://www.imooc.com/article/20310
來源:慕課網(wǎng)
本文原創(chuàng)發(fā)布于慕課網(wǎng) ,轉(zhuǎn)載請注明出處,謝謝合作!


易優(yōu)小程序(企業(yè)版)+靈活api+前后代碼開源 碼云倉庫:starfork
本文地址:http://22321a.com/wxmini/doc/course/18358.html 復(fù)制鏈接 如需定制請聯(lián)系易優(yōu)客服咨詢:800182392 點擊咨詢
QQ在線咨詢