原文地址: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相關的知識
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。參與簽名的字段名爲appid,partnerid,prepayid,noncestr,timestamp,package。注意: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 =