微信小程序ofo小黃車+thinkphp5.0打造全棧應用

原文地址:https://www.imooc.com/article/20310php

ofo至今尚未微信小程序(很費解),每次用ofo都得去支付寶,很不方便,我用微信用的比較多,無心間在簡書上面看到某人寫了一個關於ofo的小程序,連接以下:給ofo小黃車擼一個微信小程序,不過數據都是模擬的,沒有數據庫,沒有後臺,這對於一個PHP攻城獅來講,是可忍孰不可忍呀,剛剛學完七月老師的課程,受益不淺,恰好本身動手作一個,說動手就動手,let's do it;前端

先獻上一波效果圖吧:mysql

體驗版頁面git

支付頁面github

計費頁面sql

開鎖頁面thinkphp

用車頁面數據庫

開鎖頁面json

充值頁面小程序

我的中心頁面

個人錢包頁面

首頁頁面

ofo小程序的架構體系:

小程序數據從服務器到前端交互總結:

數據庫設計:

用戶表:

**user  | CREATE TABLE `user` (** `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `openid` varchar(50) NOT NULL COMMENT '用戶的惟一標識', `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 '經度', `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 '經度', `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 '結束時間', `start_time` int(11) NOT NULL COMMENT '開始時間', `total_price` decimal(10,0) NOT NULL COMMENT '總價格', `start_long` varchar(50) NOT NULL COMMENT '開始經度', `start_lati` varchar(50) NOT NULL COMMENT '開始緯度', `end_long` varchar(50) NOT NULL COMMENT '結束經度', `end_lati` varchar(50) NOT NULL COMMENT '結束緯度', `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相關的知識

  • TP5三大核心:路由、控制器、模型
  • 以ORM的方式查詢數據庫
  • 使用TP5驗證器Validate構建整個驗證層
  • 開發環境和生產環境下不一樣的全局異常處理機制
  • TP5緩存的使用
  • 在TP5中使用數據庫事務
    2.微信小程序+微信支付
  • 微信小程序登陸狀態維護
  • 微信支付接入
  • Class和Module面向對象的思惟構建前端代碼
  • 體驗優化
    3.API接口的設計
  • 採用RESTFul API風格
  • (RESTFul API風格可參考GitHub 開發者文檔)
  • 返回碼、URL語義、HTTP動詞、錯誤碼、異常返回
  • 使用Token令牌來構建用戶受權體系
  • API版本控制(v一、v2)

ofo頁面邏輯和所需接口分析

1.首頁頁面邏輯與接口分析

根據效果圖,很明顯咱們知道確定須要一個獲取單車信息的接口,接口代碼以下:

/** * @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令牌有有效期),若是令牌存在,咱們還得判斷令牌是否有效,不然從新登陸,若是驗證經過,咱們還得判斷這個用戶是否已經有押金,若是沒有押金,跳到充值頁面去充值,不然跳轉到用車頁面,根據分析,咱們須要一個驗證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已通過期', '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; }

故障按鈕分析:一樣的咱們須要驗證是否登陸,登陸是否過時,不然咱們跳轉到登陸頁面。(注意:咱們須要把用戶的初始位置,記錄到小程序的緩存中,由於騎行記錄表須要記錄用戶的初始位置)

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

關於使用token令牌的好處,請自行百度,首先我先用一張圖來講明微信小程序如何獲取token:

根據效果圖,咱們須要獲取token令牌接口,接口代碼以下:

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

設置token的有效期,把token存儲在服務器端的緩存中,返回token,客戶端獲取到token,存儲到緩存中,雙向存儲token,之後每次訪問接口都攜帶token,更加安全,有效的防止有人僞造token獲取接口的信息

3.我的中心頁面邏輯和所需接口分析

根據效果圖,點擊個人錢包按鈕須要跳轉到個人錢包頁面,咱們須要一個獲取用戶信息的接口,接口代碼以下:

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

退出登陸按鈕:咱們須要刪除本地token,跳轉到登陸頁面

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

根據效果圖:咱們須要一個充值的接口,由於是我的開發,沒有商戶號,因此微信支付就沒有作,不過其實微信支付也並不難,附上微信支付的流程:

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

這個接口須要注意的是,從哪一個頁面過來的,從首頁過來的,應該就是押金充值,從個人錢包頁面和支付頁面過來的,就應該是餘額充值,根據form不一樣,咱們數據庫充值記錄表裏面的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.當即用車頁面邏輯與接口分析

根據效果圖,咱們須要一個獲取單車密碼的接口,根據用戶輸入的ID,獲取單車的信息,若是is_show爲1,服務器拋出自定義的異常,單車正在被使用,type爲1,單車被報修,出現故障,不能使用,單車若是不存在,拋出異常,單車不存在。獲取到單車的密碼後,攜帶密碼和單車號到結果頁面,接口代碼以下:

/** * @param $id * @return array|false|\PDOStatement|string|\think\Model * @throws BikeException * 根據單車的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.計時頁面邏輯和接口分析

根據效果圖:計時開始時,咱們須要把單車的使用狀態改變,改變爲正在使用狀態,接口代碼以下:

/** * @param $id * 修改單車的使用狀態 */ public function updateBikeStatus($type = 0,$id) { // (new IsMustBePostiveInt())->goCheck(); if($type == 0) { //鎖定單車,單車在被使用中 $data = [ 'is_show'=>1 ]; }elseif ($type == 1) { //釋放單車,單車恢復使用 $data = [ 'is_show'=>0 ]; }elseif ($type == 2) { //單車出現故障 $data = [ 'type'=>1 ]; }elseif ($type == 3) { //單車恢復正常 $data = [ 'type'=>0 ]; } $res = \app\api\model\Bike::update($data,['id'=>$id]); if($res) { return true; }else { echo false; } }

7.故障頁面邏輯和接口分析

根據效果圖,咱們首先須要一個獲取故障分類名稱的接口,接口代碼以下:

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

而後提交的時候,咱們須要一個記錄故障的接口,這個接口中,咱們首先須要判斷,若是沒有選擇車牌損壞,則必須填寫車牌號,不然服務器返回自定義的異常,請輸入單車號,單車和故障很明顯是多對多的關係,咱們在記錄的時候,還要寫到另一張表中去,有記錄ID和分類ID組成的主鍵的表,同時咱們根據單車的ID還得修改單車的狀態,接口代碼以下:

public function recordTrouble($record) { //分爲兩種狀況,車牌損壞,車牌未損壞 //若是有車牌號碼,先判斷單車是否存在,不存在,拋出異常, //若是存在,寫到trouble_record表,根據trouble_record //的id,還有trouble_id寫到bike_trouble表,多對多表,所有寫入成功以後, //修改bike表的type值,用到事務,要麼失敗,要麼成功 $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) { //修改單車的狀態,發送了故障 $bike = new Bike(); $bike->updateBikeStatus(2,$bikeID); } if($resID && $rel) { Db::commit(); } }catch (Exception $e) { Db::rollback(); } }

8.支付頁面的邏輯和接口分析

根據效果圖:咱們須要一個記錄騎行的接口,這個接口中,這裏有對多張表的操做,因此咱們利用了tp的事務(注意:mysql數據引擎MyISAM不支持事務),提升數據庫數據的一致性,咱們須要記錄用戶的開始地址,開始時間,結束地址,結束時間,總價格,用戶的id,單車的id等等,咱們還須要修改用戶表的餘額,同時修改小程序緩存的餘額,關鍵點的是,咱們還要再次獲取用戶的地址,及時修改單車的使用狀態和位置,便於其餘用戶的使用,小黃車沒有GPS定位系統,而是巧妙的利用了用戶的地址,這裏咱們看下小黃車的整個使用流程:

接口代碼以下:

 /** * @param $start_time * @param $bikeID * @param $end_time * @param $start_long * @param $start_lati * @param $end_long * @param $end_lati * @param $price * 用戶騎行後記錄到數據庫 */ 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 { //建立記錄 $res = Record::create($data); //修改用戶的餘額 $user = new UserModel(); $userInfo = $user->find($uid); $data = [ 'balance'=>$userInfo->balance-$price ]; $rel = $user->save($data,['id'=>$uid]); //修改小黃車的狀態和位置 $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(); } }

結語

到這裏,ofo小程序的製做就到了尾聲了。開篇咱們簡單進行了數據庫的設計,而後一個一個頁面從頁面分析,到完成接口設計,分別響應着不一樣的業務邏輯,有的頁面與頁面之間有數據往來,咱們就經過跳轉頁面傳參或設置本地存儲來將它們創建起聯繫,環環相扣,構建起了整個小程序的基本功能,使本來的ofo小程序有了靈魂。
首先感謝慕課網和慕課網的講師七月老師,微信小程序商城構建全棧應用這門課程對我一個還沒畢業,尚未什麼工做經驗的小白來講影響很大,改變了我對傳統互聯網的見解,先後端分離,使分工更加明確,後端工程師只要專一於數據和業務,這個項目作完,使我對先後端分離理解深入,注意代碼的複用性,實踐纔是王道,這個項目採用了tp5框架,自定義了全局異常類,自定義驗證器,加深了我對AOP思想的理解,使用restful API設計接口,更加符合規範。
源碼在個人github主頁上面,須要的請移步下載github連接,若是喜歡,請給一個start,謝謝


做者: 微末凡塵卻心向天空 
連接:https://www.imooc.com/article/20310
來源:慕課網
本文原創發佈於慕課網 ,轉載請註明出處,謝謝合做!

相關文章
相關標籤/搜索