itfantasy

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

ofo至今还没有微信小程序(很费解),每次用ofo都得去支付宝,很不方便,我用微信用的比较多,无意间在简书上面看到某人写了一个关于ofo的小程序,链接如下:给ofo小黄车撸一个微信小程序,不过数据都是模拟的,没有数据库,没有后台,这对于一个PHP攻城狮来说,是可忍孰不可忍呀,刚刚学完七月老师的课程,受益匪浅,刚好自己动手做一个,说动手就动手,let\'s do it;

先献上一波效果图吧:

体验版页面

支付页面

计费页面

开锁页面

用车页面

开锁页面

充值页面

个人中心页面

我的钱包页面

首页页面

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。参与签名的字段名为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 

分类:

技术点:

相关文章:

  • 2021-12-05
  • 2021-09-19
  • 2021-08-25
  • 2021-11-03
  • 2021-06-20
  • 2021-06-21
  • 2022-01-06
  • 2021-11-27
猜你喜欢
  • 2022-02-10
  • 2022-12-23
  • 2022-12-23
  • 2021-11-17
  • 2022-01-07
  • 2022-01-22
相关资源
相似解决方案