Repository: yefangyong/ofo Branch: master Commit: 1cd2db9ed876 Files: 343 Total size: 2.2 MB Directory structure: gitextract_m6p0u8d2/ ├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── application/ │ ├── .htaccess │ ├── admin/ │ │ ├── config.php │ │ ├── controller/ │ │ │ ├── Base.php │ │ │ ├── Bike.php │ │ │ ├── Charge.php │ │ │ ├── Index.php │ │ │ ├── Record.php │ │ │ ├── Trouble.php │ │ │ └── User.php │ │ ├── model/ │ │ │ ├── Bike.php │ │ │ ├── BikeTrouble.php │ │ │ ├── Charge.php │ │ │ ├── Record.php │ │ │ ├── TroubleCate.php │ │ │ ├── TroubleRecord.php │ │ │ └── User.php │ │ └── view/ │ │ ├── bike/ │ │ │ ├── add.html │ │ │ └── index.html │ │ ├── charge/ │ │ │ └── index.html │ │ ├── index/ │ │ │ └── index.html │ │ ├── public/ │ │ │ ├── footer.html │ │ │ ├── header.html │ │ │ └── menu.html │ │ ├── record/ │ │ │ └── index.html │ │ ├── trouble/ │ │ │ ├── add.html │ │ │ ├── index.html │ │ │ └── troublecate.html │ │ └── user/ │ │ └── index.html │ ├── api/ │ │ ├── controller/ │ │ │ └── v1/ │ │ │ ├── Base.php │ │ │ ├── Bike.php │ │ │ ├── Token.php │ │ │ ├── TroubleCate.php │ │ │ ├── TroubleRecord.php │ │ │ └── User.php │ │ ├── model/ │ │ │ ├── Base.php │ │ │ ├── Bike.php │ │ │ ├── BikeTrouble.php │ │ │ ├── Charge.php │ │ │ ├── Record.php │ │ │ ├── TroubleCate.php │ │ │ ├── TroubleRecord.php │ │ │ └── User.php │ │ ├── service/ │ │ │ ├── Token.php │ │ │ └── UserToken.php │ │ └── validate/ │ │ ├── AddressNews.php │ │ ├── AppTokenGet.php │ │ ├── BaseValidate.php │ │ ├── Count.php │ │ ├── IDCollection.php │ │ ├── IsMustBePostiveInt.php │ │ ├── OrderPlace.php │ │ ├── PagingParameter.php │ │ └── TokenGet.php │ ├── command.php │ ├── common.php │ ├── config.php │ ├── database.php │ ├── extra/ │ │ ├── map.php │ │ ├── pagination.php │ │ ├── queue.php │ │ ├── redis.php │ │ ├── secure.php │ │ ├── setting.php │ │ ├── tx.php │ │ └── wx.php │ ├── index/ │ │ └── controller/ │ │ └── Index.php │ ├── lib/ │ │ └── exception/ │ │ ├── BaseException.php │ │ ├── BikeException.php │ │ ├── ExceptionHandler.php │ │ ├── ParameterException.php │ │ ├── SuccessMessage.php │ │ ├── TokenException.php │ │ ├── UserException.php │ │ └── WxChatException.php │ ├── route.php │ └── tags.php ├── build.php ├── composer.json ├── extend/ │ ├── .gitignore │ ├── Map.php │ ├── My/ │ │ └── RedisPackage.php │ ├── TxMap.php │ └── Weixin.php ├── ofo 无微信支付/ │ ├── README.md │ ├── app.js │ ├── app.json │ ├── app.wxss │ ├── pages/ │ │ ├── billing/ │ │ │ ├── index.js │ │ │ ├── index.json │ │ │ ├── index.wxml │ │ │ └── index.wxss │ │ ├── charge/ │ │ │ ├── index.js │ │ │ ├── index.json │ │ │ ├── index.wxml │ │ │ └── index.wxss │ │ ├── index/ │ │ │ ├── index.js │ │ │ ├── index.json │ │ │ ├── index.wxml │ │ │ └── index.wxss │ │ ├── input/ │ │ │ ├── index.js │ │ │ ├── index.json │ │ │ ├── index.wxml │ │ │ └── index.wxss │ │ ├── logs/ │ │ │ ├── logs.js │ │ │ ├── logs.json │ │ │ ├── logs.wxml │ │ │ └── logs.wxss │ │ ├── my/ │ │ │ ├── index.js │ │ │ ├── index.json │ │ │ ├── index.wxml │ │ │ └── index.wxss │ │ ├── pay/ │ │ │ ├── index.js │ │ │ ├── index.json │ │ │ ├── index.wxml │ │ │ └── index.wxss │ │ ├── scanresult/ │ │ │ ├── index.js │ │ │ ├── index.json │ │ │ ├── index.wxml │ │ │ └── index.wxss │ │ ├── unlock/ │ │ │ ├── index.js │ │ │ ├── index.json │ │ │ ├── index.wxml │ │ │ └── index.wxss │ │ ├── wallet/ │ │ │ ├── index.js │ │ │ ├── index.json │ │ │ ├── index.wxml │ │ │ └── index.wxss │ │ └── warn/ │ │ ├── index.js │ │ ├── index.json │ │ ├── index.wxml │ │ └── index.wxss │ ├── project.config.json │ └── utils/ │ ├── av-weapp-min.js │ ├── base.js │ ├── config.js │ └── util.js ├── phpunit.xml ├── public/ │ ├── .htaccess │ ├── index.php │ ├── robots.txt │ ├── router.php │ └── static/ │ ├── .gitignore │ ├── admin/ │ │ ├── common.css │ │ └── js/ │ │ ├── common.js │ │ └── dialog.js │ ├── h-ui/ │ │ ├── css/ │ │ │ ├── H-ui.css │ │ │ ├── H-ui.ie.css │ │ │ └── H-ui.reset.css │ │ └── js/ │ │ └── H-ui.js │ ├── h-ui.admin/ │ │ ├── css/ │ │ │ ├── H-ui.admin.css │ │ │ ├── H-ui.login.css │ │ │ └── style.css │ │ ├── js/ │ │ │ ├── H-ui.admin.js │ │ │ └── de_DE.txt │ │ └── skin/ │ │ ├── blue/ │ │ │ └── skin.css │ │ ├── default/ │ │ │ └── skin.css │ │ ├── green/ │ │ │ └── skin.css │ │ ├── orange/ │ │ │ └── skin.css │ │ ├── red/ │ │ │ └── skin.css │ │ └── yellow/ │ │ └── skin.css │ ├── js/ │ │ ├── common.js │ │ ├── login.js │ │ └── order.js │ └── lib/ │ ├── DD_belatedPNG_0.0.8a-min.js │ ├── Hui-iconfont/ │ │ └── 1.0.8/ │ │ ├── demo.html │ │ └── iconfont.css │ ├── html5shiv.js │ ├── jquery/ │ │ └── 1.9.1/ │ │ └── jquery.js │ └── layer/ │ └── 2.4/ │ ├── layer.js │ └── skin/ │ └── layer.css ├── runtime/ │ └── .gitignore ├── tests/ │ ├── ExampleTest.php │ └── TestCase.php ├── think ├── thinkphp/ │ ├── .gitignore │ ├── .htaccess │ ├── .travis.yml │ ├── CONTRIBUTING.md │ ├── LICENSE.txt │ ├── README.md │ ├── base.php │ ├── codecov.yml │ ├── composer.json │ ├── console.php │ ├── convention.php │ ├── helper.php │ ├── lang/ │ │ └── zh-cn.php │ ├── library/ │ │ ├── think/ │ │ │ ├── App.php │ │ │ ├── Build.php │ │ │ ├── Cache.php │ │ │ ├── Collection.php │ │ │ ├── Config.php │ │ │ ├── Console.php │ │ │ ├── Controller.php │ │ │ ├── Cookie.php │ │ │ ├── Db.php │ │ │ ├── Debug.php │ │ │ ├── Env.php │ │ │ ├── Error.php │ │ │ ├── Exception.php │ │ │ ├── File.php │ │ │ ├── Hook.php │ │ │ ├── Lang.php │ │ │ ├── Loader.php │ │ │ ├── Log.php │ │ │ ├── Model.php │ │ │ ├── Paginator.php │ │ │ ├── Process.php │ │ │ ├── Request.php │ │ │ ├── Response.php │ │ │ ├── Route.php │ │ │ ├── Session.php │ │ │ ├── Template.php │ │ │ ├── Url.php │ │ │ ├── Validate.php │ │ │ ├── View.php │ │ │ ├── cache/ │ │ │ │ ├── Driver.php │ │ │ │ └── driver/ │ │ │ │ ├── File.php │ │ │ │ ├── Lite.php │ │ │ │ ├── Memcache.php │ │ │ │ ├── Memcached.php │ │ │ │ ├── Redis.php │ │ │ │ ├── Sqlite.php │ │ │ │ ├── Wincache.php │ │ │ │ └── Xcache.php │ │ │ ├── config/ │ │ │ │ └── driver/ │ │ │ │ ├── Ini.php │ │ │ │ ├── Json.php │ │ │ │ └── Xml.php │ │ │ ├── console/ │ │ │ │ ├── Command.php │ │ │ │ ├── Input.php │ │ │ │ ├── LICENSE │ │ │ │ ├── Output.php │ │ │ │ ├── bin/ │ │ │ │ │ └── README.md │ │ │ │ ├── command/ │ │ │ │ │ ├── Build.php │ │ │ │ │ ├── Clear.php │ │ │ │ │ ├── Help.php │ │ │ │ │ ├── Lists.php │ │ │ │ │ ├── Make.php │ │ │ │ │ ├── make/ │ │ │ │ │ │ ├── Controller.php │ │ │ │ │ │ ├── Model.php │ │ │ │ │ │ └── stubs/ │ │ │ │ │ │ ├── controller.plain.stub │ │ │ │ │ │ ├── controller.stub │ │ │ │ │ │ └── model.stub │ │ │ │ │ └── optimize/ │ │ │ │ │ ├── Autoload.php │ │ │ │ │ ├── Config.php │ │ │ │ │ ├── Route.php │ │ │ │ │ └── Schema.php │ │ │ │ ├── input/ │ │ │ │ │ ├── Argument.php │ │ │ │ │ ├── Definition.php │ │ │ │ │ └── Option.php │ │ │ │ └── output/ │ │ │ │ ├── Ask.php │ │ │ │ ├── Descriptor.php │ │ │ │ ├── Formatter.php │ │ │ │ ├── Question.php │ │ │ │ ├── descriptor/ │ │ │ │ │ └── Console.php │ │ │ │ ├── driver/ │ │ │ │ │ ├── Buffer.php │ │ │ │ │ ├── Console.php │ │ │ │ │ └── Nothing.php │ │ │ │ ├── formatter/ │ │ │ │ │ ├── Stack.php │ │ │ │ │ └── Style.php │ │ │ │ └── question/ │ │ │ │ ├── Choice.php │ │ │ │ └── Confirmation.php │ │ │ ├── controller/ │ │ │ │ ├── Rest.php │ │ │ │ └── Yar.php │ │ │ ├── db/ │ │ │ │ ├── Builder.php │ │ │ │ ├── Connection.php │ │ │ │ ├── Query.php │ │ │ │ ├── builder/ │ │ │ │ │ ├── Mysql.php │ │ │ │ │ ├── Pgsql.php │ │ │ │ │ ├── Sqlite.php │ │ │ │ │ └── Sqlsrv.php │ │ │ │ ├── connector/ │ │ │ │ │ ├── Mysql.php │ │ │ │ │ ├── Pgsql.php │ │ │ │ │ ├── Sqlite.php │ │ │ │ │ ├── Sqlsrv.php │ │ │ │ │ └── pgsql.sql │ │ │ │ └── exception/ │ │ │ │ ├── BindParamException.php │ │ │ │ ├── DataNotFoundException.php │ │ │ │ └── ModelNotFoundException.php │ │ │ ├── debug/ │ │ │ │ ├── Console.php │ │ │ │ └── Html.php │ │ │ ├── exception/ │ │ │ │ ├── ClassNotFoundException.php │ │ │ │ ├── DbException.php │ │ │ │ ├── ErrorException.php │ │ │ │ ├── Handle.php │ │ │ │ ├── HttpException.php │ │ │ │ ├── HttpResponseException.php │ │ │ │ ├── PDOException.php │ │ │ │ ├── RouteNotFoundException.php │ │ │ │ ├── TemplateNotFoundException.php │ │ │ │ ├── ThrowableError.php │ │ │ │ └── ValidateException.php │ │ │ ├── log/ │ │ │ │ └── driver/ │ │ │ │ ├── File.php │ │ │ │ ├── Socket.php │ │ │ │ └── Test.php │ │ │ ├── model/ │ │ │ │ ├── Collection.php │ │ │ │ ├── Merge.php │ │ │ │ ├── Pivot.php │ │ │ │ ├── Relation.php │ │ │ │ └── relation/ │ │ │ │ ├── BelongsTo.php │ │ │ │ ├── BelongsToMany.php │ │ │ │ ├── HasMany.php │ │ │ │ ├── HasManyThrough.php │ │ │ │ ├── HasOne.php │ │ │ │ ├── MorphMany.php │ │ │ │ ├── MorphOne.php │ │ │ │ ├── MorphTo.php │ │ │ │ └── OneToOne.php │ │ │ ├── paginator/ │ │ │ │ └── driver/ │ │ │ │ └── Bootstrap.php │ │ │ ├── process/ │ │ │ │ ├── Builder.php │ │ │ │ ├── Utils.php │ │ │ │ ├── exception/ │ │ │ │ │ ├── Failed.php │ │ │ │ │ └── Timeout.php │ │ │ │ └── pipes/ │ │ │ │ ├── Pipes.php │ │ │ │ ├── Unix.php │ │ │ │ └── Windows.php │ │ │ ├── response/ │ │ │ │ ├── Json.php │ │ │ │ ├── Jsonp.php │ │ │ │ ├── Redirect.php │ │ │ │ ├── View.php │ │ │ │ └── Xml.php │ │ │ ├── session/ │ │ │ │ └── driver/ │ │ │ │ ├── Memcache.php │ │ │ │ ├── Memcached.php │ │ │ │ └── Redis.php │ │ │ ├── template/ │ │ │ │ ├── TagLib.php │ │ │ │ ├── driver/ │ │ │ │ │ └── File.php │ │ │ │ └── taglib/ │ │ │ │ └── Cx.php │ │ │ └── view/ │ │ │ └── driver/ │ │ │ ├── Php.php │ │ │ └── Think.php │ │ └── traits/ │ │ ├── controller/ │ │ │ └── Jump.php │ │ ├── model/ │ │ │ └── SoftDelete.php │ │ └── think/ │ │ └── Instance.php │ ├── phpunit.xml │ ├── start.php │ └── tpl/ │ ├── default_index.tpl │ ├── dispatch_jump.tpl │ ├── page_trace.tpl │ └── think_exception.tpl └── vendor/ └── .gitignore ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .idea composer.lock *.log ================================================ FILE: .travis.yml ================================================ sudo: false language: php branches: only: - stable cache: directories: - $HOME/.composer/cache before_install: - composer self-update install: - composer install --no-dev --no-interaction --ignore-platform-reqs - zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Core.zip . - composer require --update-no-dev --no-interaction "topthink/think-image:^1.0" - composer require --update-no-dev --no-interaction "topthink/think-migration:^1.0" - composer require --update-no-dev --no-interaction "topthink/think-captcha:^1.0" - composer require --update-no-dev --no-interaction "topthink/think-mongo:^1.0" - composer require --update-no-dev --no-interaction "topthink/think-worker:^1.0" - composer require --update-no-dev --no-interaction "topthink/think-helper:^1.0" - composer require --update-no-dev --no-interaction "topthink/think-queue:^1.0" - composer require --update-no-dev --no-interaction "topthink/think-angular:^1.0" - composer require --dev --update-no-dev --no-interaction "topthink/think-testing:^1.0" - zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Full.zip . script: - php think unit deploy: provider: releases api_key: secure: TSF6bnl2JYN72UQOORAJYL+CqIryP2gHVKt6grfveQ7d9rleAEoxlq6PWxbvTI4jZ5nrPpUcBUpWIJHNgVcs+bzLFtyh5THaLqm39uCgBbrW7M8rI26L8sBh/6nsdtGgdeQrO/cLu31QoTzbwuz1WfAVoCdCkOSZeXyT/CclH99qV6RYyQYqaD2wpRjrhA5O4fSsEkiPVuk0GaOogFlrQHx+C+lHnf6pa1KxEoN1A0UxxVfGX6K4y5g4WQDO5zT4bLeubkWOXK0G51XSvACDOZVIyLdjApaOFTwamPcD3S1tfvuxRWWvsCD5ljFvb2kSmx5BIBNwN80MzuBmrGIC27XLGOxyMerwKxB6DskNUO9PflKHDPI61DRq0FTy1fv70SFMSiAtUv9aJRT41NQh9iJJ0vC8dl+xcxrWIjU1GG6+l/ZcRqVx9V1VuGQsLKndGhja7SQ+X1slHl76fRq223sMOql7MFCd0vvvxVQ2V39CcFKao/LB1aPH3VhODDEyxwx6aXoTznvC/QPepgWsHOWQzKj9ftsgDbsNiyFlXL4cu8DWUty6rQy8zT2b4O8b1xjcwSUCsy+auEjBamzQkMJFNlZAIUrukL/NbUhQU37TAbwsFyz7X0E/u/VMle/nBCNAzgkMwAUjiHM6FqrKKBRWFbPrSIixjfjkCnrMEPw= file: - ThinkPHP_Core.zip - ThinkPHP_Full.zip skip_cleanup: true on: tags: true ================================================ FILE: LICENSE.txt ================================================ ThinkPHP遵循Apache2开源协议发布,并提供免费使用。 版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn) All rights reserved。 ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。 Apache Licence是著名的非盈利开源组织Apache采用的协议。 该协议和BSD类似,鼓励代码共享和尊重原作者的著作权, 允许代码修改,再作为开源或商业软件发布。需要满足 的条件: 1. 需要给代码的用户一份Apache Licence ; 2. 如果你修改了代码,需要在被修改的文件中说明; 3. 在延伸的代码中(修改和有源代码衍生的代码中)需要 带有原来代码中的协议,商标,专利声明和其他原来作者规 定需要包含的说明; 4. 如果再发布的产品中包含一个Notice文件,则在Notice文 件中需要带有本协议内容。你可以在Notice中增加自己的 许可,但不可以表现为对Apache Licence构成更改。 具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.md ================================================ # 简介 本项目是采用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.首页页面逻辑与接口分析  根据效果图,很明显我们知道肯定需要一个获取单车信息的接口,接口代码如下: ```php /** * @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是否有效的接口,接口代码如下, ```php /** * @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; } ``` 我们还需要一个获取用户信息的接口,判断是否有押金,接口代码如下: ```php /** * @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令牌接口,接口代码如下: ```php /** * @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.个人中心页面逻辑和所需接口分析  根据效果图,点击我的钱包按钮需要跳转到我的钱包页面,我们需要一个获取用户信息的接口,接口代码如下: ```php /** * @return null|static * @throws UserException * 获取用户的信息 */ public function getUserInfo(){ $uid = Token::getCurrentUid(); $user = UserModel::get($uid); if(!$user) { throw new UserException(); } return $user; } ``` 退出登录按钮:我们需要删除本地token,跳转到登录页面 ## 4.充值页面逻辑和接口分析 根据效果图:我们需要一个充值的接口,因为是个人开发,没有商户号,所以微信支付就没有做,不过其实微信支付也并不难,附上微信支付的流程: ```php 商户系统和微信支付系统主要交互说明: 步骤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为押金充值,接口代码如下: ```php /** * @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,单车被报修,出现故障,不能使用,单车如果不存在,抛出异常,单车不存在。获取到单车的密码后,携带密码和单车号到结果页面,接口代码如下: ```php /** * @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.计时页面逻辑和接口分析  根据效果图:计时开始时,我们需要把单车的使用状态改变,改变为正在使用状态,接口代码如下: ```php /** * @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.故障页面逻辑和接口分析 根据效果图,我们首先需要一个获取故障分类名称的接口,接口代码如下: ```php /** * @return false|\PDOStatement|string|\think\Collection * 获取问题的分类信息 */ public function getTroubleCate() { $res = new \app\api\model\TroubleCate(); $troubleCate = $res->select(); return $troubleCate; } ``` 然后提交的时候,我们需要一个记录故障的接口,这个接口中,我们首先需要判断,如果没有选择车牌损坏,则必须填写车牌号,否则服务器返回自定义的异常,请输入单车号,单车和故障很明显是多对多的关系,我们在记录的时候,还要写到另外一张表中去,有记录ID和分类ID组成的主键的表,同时我们根据单车的ID还得修改单车的状态,接口代码如下: ```php 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链接](https://github.com/yefangyong/ofo),如果喜欢,请给一个start,谢谢 ================================================ FILE: application/.htaccess ================================================ deny from all ================================================ FILE: application/admin/config.php ================================================ 'html' ]; ================================================ FILE: application/admin/controller/Base.php ================================================ param(); if(!intval($params['id'])){ return show(0,'id不合法'); } //获取当前控制器的名字,一般控制器名字和model和数据表名字一直,如果不一致,我们可以在model里面,直接声明model //例如 $this->model = 'model名' $model = $this->model?$this->model:request()->controller(); try { $res = model($model)->save(['status'=>$params['status']],['id'=>$params['id']]); }catch (Exception $e) { return show(0,$e->getMessage()); } $trouble = model($model)->get($params['id']); if(isset($trouble->bike_id ) && $trouble->bike_id != 0 ) { try { $rel = model('Bike')->save(['type'=>0],['id'=>$trouble->bike_id ]); }catch (Exception $e) { return show(0,$e->getMessage()); } } if($res) { return show(1,'操作成功'); }else { return show(0,'操作失败'); } } } ================================================ FILE: application/admin/controller/Bike.php ================================================ fetch('',['bike'=>$bikes]); } public function add() { if($_POST) { $passwd = $_POST['password']; $address = $_POST['address']; if(empty($passwd) || empty($address)) { return show(0,'请填写密码或者地址'); } $result = \TxMap::getLngLat($address); $lat = $result['result']['location']['lat']; $lng = $result['result']['location']['lng']; $data = [ 'latitude'=>$lat, 'longitude'=>$lng, 'is_show'=>0, 'type'=>0, 'password'=>$passwd ]; $res = \app\admin\model\Bike::create($data); if($res) { return show(1,'添加成功'); }else { return show(0,'添加失败'); } }else { return $this->fetch(); } } } ================================================ FILE: application/admin/controller/Charge.php ================================================ fetch('',['charge'=>$charge]); } } ================================================ FILE: application/admin/controller/Index.php ================================================ fetch(); } public function welcome() { return "欢迎访问享骑单车小程序后台管理系统"; } } ================================================ FILE: application/admin/controller/Record.php ================================================ fetch('',['records'=>$records]); } } ================================================ FILE: application/admin/controller/Trouble.php ================================================ $val) { foreach ($val['trouble_cate'] as $value) { $reason .= ','.$value['name']; } $reason = substr($reason,1); $troubles[$key]['reason'] = $reason; } return $this->fetch('',['trouble'=>$troubles]); } /** * @return mixed * 故障分类列表 */ public function troubleCate() { $result = TroubleCate::getTroubleCate(); return $this->fetch('',['troubleCate'=>$result]); } /** * @return mixed|void * 分类添加 */ public function add(){ if($_POST) { if(empty($_POST['catename'])) { return show(0,'请输入分类名称'); }else { $data = [ 'name'=>$_POST['catename'], ]; $res = TroubleCate::create($data); if($res) { return show(1,'添加成功'); }else { return show(0,'添加失败'); } } }else{ return $this->fetch(); } } } ================================================ FILE: application/admin/controller/User.php ================================================ fetch('',['user'=>$user]); } } ================================================ FILE: application/admin/model/Bike.php ================================================ paginate(config('pagination.list_rows'))->each(function ($item, $key){ $lnt = $item->latitude; $lgt = $item->longitude; $result = \TxMap::getAddress($lgt,$lnt); $item->address = $result['result']['formatted_addresses']['recommend']; return $item; }); } } ================================================ FILE: application/admin/model/BikeTrouble.php ================================================ belongsTo('User','user_id'); } /** * @return \think\Paginator * 获取所有的充值记录 */ public static function getAllCharge() { $pagination = config('pagination.charge'); return self::order('create_time','desc')->with('user')->paginate($pagination['list_row']); } } ================================================ FILE: application/admin/model/Record.php ================================================ belongsTo('bike','bike_id'); } /** * @return \think\model\relation\BelongsTo * 关联用户表 */ public function user() { return $this->belongsTo('user','user_id'); } /** * @return $this * 获取所有的记录 */ public static function getAllRecord() { return self::order('create_time','desc')->with(['bike','user'])->paginate(config('pagination.list_rows'))->each(function($item,$key){ $slnt = $item->start_lati; $slgt = $item->start_long; $sresult = \TxMap::getAddress($slgt,$slnt); if($sresult['status'] !=0) { $item->start_address = '' ; }else { $item->start_address = $sresult['result']['formatted_addresses']['recommend']; } $elnt = $item->end_lati; $elgt = $item->end_long; $eresult = \TxMap::getAddress($elgt,$elnt); if($eresult['status'] !=0) { $item->end_address = '' ; }else { $item->end_address = $eresult['result']['formatted_addresses']['recommend']; } return $item; }); } } ================================================ FILE: application/admin/model/TroubleCate.php ================================================ paginate(config('pagination.list_rows')); } } ================================================ FILE: application/admin/model/TroubleRecord.php ================================================ belongsToMany('troubleCate','bike_trouble','trouble_id','record_id'); } /** * @return \think\model\relation\BelongsTo * 关联用户表 */ public function user() { return $this->belongsTo('user','user_id'); } /** * @return $this * 获取所有的故障列表 */ public static function getAllTrouble(){ return self::order('create_time','desc')->with(['troubleCate','user','troubleCate'])->paginate(config('pagination.list_rows'))->each(function($item,$key){ $lnt = $item->latitude; $lgt = $item->longitude; $result = \TxMap::getAddress($lgt,$lnt); if($result['status'] != 0) { $item->address = '' ; }else { $item->address = $result['result']['formatted_addresses']['recommend']; } return $item; }); } } ================================================ FILE: application/admin/model/User.php ================================================ paginate($pagination['list_row']); } } ================================================ FILE: application/admin/view/bike/add.html ================================================ {include file="public/header" /}