链接

简介

本项目是采用PHP语言,THINKPHP5.0框架开发的全栈应用系统。在开发这个项目时,微信还没有OFO微信小程序,又不想下载APP去使用,只能在支付宝上面使用OFO,偶然间发现某人写了一个关于OFO的微信小程序,链接如下:给ofo小黄车撸一个微信小程序,数据是模拟的,没有数据库,没有后台,所以我基于这个前端项目,设计了数据库,提供了数据支持和后台服务。

项目截图

体验版页面

支付页面

首页页面

用车页面

开锁页面

开锁页面

计费页面

充值页面

个人中心页面

我的钱包页面

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版本控制(v1、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。参与签名的字段名为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 = 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,谢谢

think PHP5.0小程序,微信小程序ofo小黄车+thinkphp5.0打造全栈应用相关推荐

  1. 恋爱小助手微信QQ双端小程序源码/可添加流量主,无需域名无需服务器

    源码介绍 恋爱小助手微信QQ双端小程序源码,无需服务器和域名,可添加流量主,搭建教程都打包在文件里面了. 源码特点: 1.支持任意漫画头像生成功能,后端自定义字体,颜色,大小,角度,位置,二维码水印等 ...

  2. PHP抽奖小程序/微信红包封面抽奖小程序/抽奖小程序开源源码

    PHP抽奖小程序源码,微信红包封面抽奖小程序,全开源.带流量主(需要自行替换代码),独立后台,亲测完美可用. 下载 PHP抽奖小程序/微信红包封面抽奖小程序/抽奖小程序开源源码 亲测截图:

  3. 最新版恋爱话术小助手微信QQ双端小程序源码+云开发+流量主+教程

    恋爱小助手微信QQ双端小程序源码 简介: 无需服务器和域名 搭建教程都打包在文件里面了 1.支持任意漫画头像生成功能,后端自定义字体,颜色,大小,角度,位置,二维码水印等信息 2.支持表白情书生成功能 ...

  4. 【微信小程序控制硬件⑦ 进阶篇】动起来做一个微信小程序Mqtt协议控制智能硬件的框架,为心里全栈工程师梦想浇水。

    文章目录 一.前言: 二.涉及的技术点: 三.框架的运行原理: 四.框架代码流程: 4.1 主线程: 4.2 获取设备列表显示设备,以及订阅在线的设备: 4.3 点击某设备如何实现携带此设备信息到控制 ...

  5. php微信外卖多店点餐小程序,微信外卖点餐小程序怎么制作?按这个步骤来就行...

    秋分已过,气温早已逐渐降温了,针对许多宅男宅女而言,又来到一个醒来靠胆量外出靠吸引力的季节,那么微信点餐就不可或缺了,因此,做餐馆的小伙伴们使用点餐小程序的话,就能吸引到大批的用户.那么微信外卖点餐小 ...

  6. 微信小程序 基础3【组件化开发、自定义组件、全栈开发、使用Express】

    视频地址: https://www.bilibili.com/video/BV1cW411T7t6  [2018]学做小程序- 清华大学 https://www.bilibili.com/video/ ...

  7. 腾讯AI开源框架Angel 3.0重磅发布:超50万行代码,支持3种算法,打造全栈机器学习平台...

    出品 | AI科技大本营(ID:rgznai100) [导语]2019年8月22日,腾讯首个AI开源项目Angel正式发布3.0版本.Angel 3.0尝试打造一个全栈的机器学习平台,功能特性涵盖了机 ...

  8. 微信小程序微信公众号支付宝小程序H5(获取信息用户信息,支付,分享,人脸识别)

    文章目录 一.微信小程序 1. 获取信息用户信息 2.支付 3.分享 4. 腾讯云小程序人脸核身 二.微信公众号 1.获取信息用户信息 2.支付 3. 分享(普通分享) 4.分享(vue单页面 配置分 ...

  9. 英雄杀小程序微信区分服务器吗,小程序英雄杀挑战模式怎么玩

    最近英雄杀这款游戏真的是非常火呢,那么各位小伙伴知道小程序英雄杀挑战模式怎么玩吗?下面就由游乐小编给各位带来微信小程序英雄杀挑战模式玩法攻略,希望对大家能够用的上~ 微信小程序英雄杀挑战模式玩法分享 ...

最新文章

  1. Expo 快速开启之旅
  2. 对于FAMNIST中的十种动物和水果进行识别测试
  3. 你不得不知的Safari 4
  4. AI「复活」《延禧攻略》众生相
  5. Python程序设计题解【蓝桥杯官网题库】 DAY15-算法训练
  6. 利用jQuery实现回收站删除效果
  7. 童年记忆-莴苣姑娘的故事
  8. C# list删除 另外list里面的元素_在Python 中 List 操作 9种例子详细了解
  9. Xcode 代码格式化/自动排版
  10. 如何查看Python的版本
  11. 201671010140. 2016-2017-2 《Java程序设计》java学习第十六周
  12. Java并发编程笔记之ArrayBlockingQueue源码分析
  13. 阿里云ddns解决动态IP问题
  14. webstorm(10.0.2)设置测试服务器 -- 局域网内其他设备访问
  15. IMPORTANT: You may need to close and restart your shell after running ‘conda init‘.
  16. win10系统IIS服务器配置详细教程,win10系统配置iis的操作方法
  17. 前端根据后端数据生成表格 行列合并 指定表头
  18. 算法的时间复杂度表示
  19. 数字信号处理(3)- z变换
  20. 【软件质量】软件安全性

热门文章

  1. snm算法_基于SNM算法的大数据量中文地址清洗方法-计算机工程与应用.PDF
  2. 小米的java待遇怎么样_【Java工资】小米2021年Java工资待遇-看准网
  3. 中国互联网十年白皮书
  4. Spring 5 注解编程基础组件
  5. 区块链公证和知识产权(IP)保护原理
  6. 如何实现自动化整理桌面
  7. 基于NFT的产品追溯实现思路
  8. Spark算子综合案例
  9. pycharm里有android虚拟设备吗,已解决!PyCharm打开一直出现Reloading generated skeletons问题...
  10. 数据结构学习笔记(2)之数据结构绪论