00
事情起源于9月。
在我漫长而又短暂的大学生涯中,最不喜欢的事是什么呢?
焊通用版。
最最不喜欢的事情是什么呢?
调通用版。
最最最不喜欢的事情是什么呢?
画通用版。
因此,在研0开学的时候,我卑微而又谦逊的和导师请求:
能不能不要做硬件,我想做软件
导师笑了笑:正好,现在有个软件要你做。
在完成了我的第一个活之后,第二个活来了。
要求:制作一个实验室管理系统,指纹认证、指纹登录,定时开关,远程预约。
02
在这之前,我用PHP写的大多是小项目,比如说比特币仪表盘,用SQL改改人民日报1946-2003的数据库,这是第一次接触大工程。
因此,首先要考虑的就是整个系统的组成。
整个系统由服务器、客户端、指纹认证、电源开关四个部分组成。
用户在完成指纹录入后,在有预约的前提下,服务器返回登录成功的数据,并打开电源开关。
时间到了之后,服务器发送到时间命令提示用户切断电源,2分钟后如果不切断电源,则记录当前用户信息。
如果用户在预约时间之前完成实验,则出门按指纹结束使用。
第一个问题是,服务器是用Linux服务器,还是Windows?
Linux服务器优点在于占用空间小、成本低;缺点是命令行不好维护,以及驱动安装和后续的优化问题;
Windows服务器优点在于方便安装操作、软件包固定、发行版唯一;缺点是网卡需要服务器网卡、占用资源大、功耗高。
由于专业软件的问题,不可能在开发机上运行Linux,因此我采用的是折中的方法,即开发用Windows,测试用Windows Subsystem for Linux,生产环境用Linux。
第二个问题,客户端用Qt,还是用VS?
这就要说起我的第一个活了。
我的第一个活是用Qt做的厨房监控以及传感系统。众所周知,Qt里面做传感器需要用VLC,而VLC是基于FFMPEG封装的一个库,而Qt的VLC已经很长时间没有更新了。并且,Qt-Vlc没有相应的编译好的库,要从源码编译。源码编译问题可就大了,根据不同的发行版、VC++的版本决定不同的编译器、链接器、解释器。最后我还花了2毛从CSDN上下了一个预编译的库,结果还是Release版本的,没法调试,因此,我之前的调试全在黑箱下面进行。
就这样在黑暗中摸索了一个月,在调整完Demo之后,我实在是受不了了,向导师提出:既然必须要VC++,也没有必须在Linux下,为什么不能直接用VS呢?
没想到,是我说晚了,其实只要功能实现,用什么语言客户并不介意。
于是,我用了3个小时,用XAML重写了我之前半个月用QML写的GUI。一边写,一边骂Qt,我是倒了八辈子霉才用QML写这个东西!
而且VLC-Csharp维护的人可不少,全都有预编译的库,并且一个回车就能搞定!
至于通信部分,又花了1个小时用Python把之前的通讯协议写好了。
最后是C#读取文件、拉直播流,然后Python通讯,C#只是一个壳子。
用什么语言已经不用多说了吧。
第三个问题,服务器用什么语言?
Python和PHP中我肯定是选PHP,虽然都是用C++写的,但是PHP的性能可比Python高多了,Php是为服务器而生,并且与Nginx的FASTCGI链接非常方便,关键是我不是很会Python和前端交互的手段。
第四个问题,纯PHP、ThinkPHP、Laravel?
纯PHP的工作流程是这样的:写HTML文件——中间插入<?php?>代码,归类整理文件,整理mod_rewrite文件,限制权限,最后测试发布;
ThinkPHP是国产框架,据说微软好的东西没学会,抛弃旧版本学的一个比一个快;
Laravel是世界上使用人数最多的框架,更新频繁,性能强大,路由表等常见功能直接封装好功能模块,写一行代码就行,缺点是不好学习。
既然要学习了,肯定要学习最好的框架,流行的框架肯定是有过人之处的。
因此,整个系统的框架如下所示:
03
从下往上说,第一个介绍指纹模块。
这个指纹模块叫Live20R,文档更新于2016年,支持的语言有C、JAVA、Active X,Android、Linux、C#等等。是不是看起来支持非常广泛?
其实事情刚好相反,上面几个语言有一个共同特点:
他们都是基于C,或者能够调用C语言的方法!
反编译了Live20R的C#库以后,我才发现,这个库所有的东西都是基于C写的,剩下的只是调用和封装了一下而已。
这也就算了,大家都是编程的,理解一下也没什么大不了。
但是文档写错就不对了吧?
这个2016年的文档,不知道是哪位程序员跑路前写的,库函数和文档没一个对的上的,至于产品宣传页里说的什么储存指纹呢,其实是把指纹存到内存里,重启软件就没了。
因此,拿到库函数以后,第一件是就是要找到存储指纹、读取指纹的两个方法,把他们储存到SQLite/MySQL,这样就能在本地进行对比了。
客户端和服务器的交互利用Json+RESTAPI,用来动态同步用户数据。
04
指纹模块是个小程序,重头戏在Laravel上。
前端
一个现代化的完整的网站必须做到前后端分离,要做到这点是要用到Laravel的。
先从前端开始介绍:
Laravel的所有前端的JS和CSS都是通过包管理程序打包成ALL.CSS以及ALL.js的。
前端用的是pjax+Bootstrap+Fontawesome,要引入这两个js包,要在Laravel目录下运行前端打包工具,Laravel-Mix,即:
npm run dev
同时,在resouce/app.scss下加入以下内容:
@import 'node_modules/@fortawesome/fontawesome-free/scss/fontawesome.scss';@import 'node_modules/@fortawesome/fontawesome-free/scss/regular.scss';@import 'node_modules/@fortawesome/fontawesome-free/scss/solid.scss';@import 'node_modules/@fortawesome/fontawesome-free/scss/brands.scss';// Variables@import 'variables';// Bootstrap@import '~bootstrap/scss/bootstrap';
注意!某些JS库有先后引用顺序,如果合并后功能不正常,就要把这个库单独拿出来单独引用。引用目录在public/js下,不是resource/js下
MVC结构
模型(Eloquent ORM),代表着存储的数据,这个模型和普通的模型概念可能不一样,叫做依赖注入,也就是数据的抽象。
软件的维护工作,本质上都是由“变化”引起的,只要软件还活着,我们就无法对抗变化,只能顺应它。而组件之间的依赖关系决定了变化的传导范围。
一般来说,当被依赖的组件变化时,其依赖者也会随之变化。软件开发最怕的就是牵一发而动全身。所幸,并不是每次变化都必然会传导给它的依赖者们。
对于具体实现细节的修改,只要没有改变其外部契约(可简单理解为接口),其依赖者就不需要修改。对于更大规模的修改,比如更换计费策略,我们是不是就无法控制其传播了?也不见得。只要我们的设计能让两者的接口保持一致,就可以把变化控制在尽可能小的范围内。
在我们的网站上,可以理解为用户这一模型不是某个特定用户,而是用户这个整体,在需要查询数据的时候,再实例化给某类查询,并返回结果。
视图,可以理解为前端,或者返回的数据的显示内容。
控制器,即后端,核心业务逻辑处理页面,控制器实例化模型,并处理数据传给视图。
路由表
下一个介绍的是Laravel的路由表(routes):
路由表是什么?比如说https://add.where.name/this-is-a-example/example?example=1
https称为协议,add称为二级域名,where.name称为顶级域名 /this-is-a-example/example称为路径,?example=1称为参数,值为1。
路径后面的值通过路由表传递给控制器,控制器再进行相应的操作。
前面提到过,路由表的方便在于不用自己写rewrite规则,方便程序归类整理,在route/web.php下添加:
Route::prefix('admin')->namespace('Admin')->group(function () {
Route::prefix('article')->group(function () {
Route::get('index', 'ArticleController@index');
Route::get('create', 'ArticleController@create');
Route::post('store', 'ArticleController@store');
});
});
就能实现在admin/article/index的get/post方法。
其中,prefix指的是网页路径,namespace指的是服务器文件路径。
路由可以编组,上面的代码指的是在Admin文件夹下的ArticleController控制器。
控制器
大家都知道,所有网站无非就是增删查改四种操作。
重复性的操作就不用我们自己写了,打开Laravel的目录,输入:
php artisan make:controller ArticleController --resource
打开这个文件,你会发现,显示,增删查改函数全部写好了。简直是太贴心了!
之后的事情就是写逻辑部分啦。
写完逻辑部分之后,记得和上面的路由表参数一样,@后面跟的是函数名,否则是进不去方法的。
这里多了一个小问题,如果路径带参数呢,比如要访问admin/1/这样的文件?
路径参数只要加个{}就行了,比如:
Route::get('{id?}', 'PermisstionController@index')->where('id', '[0-9]+');
这句话的意思是{id}参数可以不带,并且在id参数下只允许数字请求。
进入PermisstionController控制器:头部函数长这样:
public function index($id=1){
……
}
这里,id参数就传进去了。$id=1的意思是参数默认的值为1。
那么,参数值怎么传进去呢?这里要用到Laravel自带的参数了:
public function update(Request $request, $id)
{
try{
$input = $request->all();
if ($input['operate']=='update_seats')}
注意,Laravel会自动进行Json解码,因此直接$input['operate']拿过来就可以,是不是既简单又方便?
数据库
依赖注入废话了半天,但是还得先说数据库:
要创建数据库,不用费劲的学SQL语言,Laravel全给你封装好了。
运行以下命令:
php artisan make:migration create_articles_table
Laravel就会在database/migrations生成一个迁移文件,打开这个文件,就能直接编辑要创建的数据表了。
参考资料的代码如下:
Schema::create('articles', function (Blueprint $table) {
$table->increments('id');
$table->integer('category_id')->unsigned()->default(0)->comment('分类id');
$table->string('title')->comment('标题');
$table->text('content')->comment('内容');
$table->timestamps();
$table->softDeletes();
});
相信应该不用过多解释了,一共就那么几种数据类型,一个回车就完事了。
表结构生成之后,输入:
php artisan migrate
你就发现Laravel自动进行迁移了。
如果发现不行了,点错了,只要输入:
php artisan migrate:rollback
就能回退上一步了。
模型
最关键的模型来了:
首先在命令行输入:
php artisan make:model Article
Article指的是上一步创建的表名,注意首字母大写,名字必须要一样!
之后输入:
php artisan make:controller ModelController
并添加路由:
Route::get('index', 'ModelController@index');
在ModelController中加入index方法:
public function index(Article $articleModel)
{
$data = $articleModel->get();
dump($data);
}
这样,Article模型就创建完成了。
咋一看有点看不懂,调用的时候就方便的很多了。
在传统的MySQL+PHP引用中,要根据PHP的版本先安装插件、然后引用插件、实例化、运行SQL语句,也就是大家都喜欢的“SELECT * FROM TABLE”;
在这里,我们只要这样:
$data = Article::select('id','name', 'email', 'provider')
->WhereBetween('id',[ $id , $id + 19 ])
->orderBy('id', 'desc')
->get();
就能获取到编码好的数据啦,而且非常的安全!
业务逻辑
业务逻辑方面,先介绍基本的流程,其实逻辑代码不多,就是RestAPI的增删查改。
- 用户注册——输入邮箱/Github登录——注册完成
- 用户登录——输入用户名密码/Github登录——登录成功
- 更改密码——发送至预留邮箱——找回密码
- 预约——选择可用教室——选择可用时段——选择可用座位——预约成功
- 取消预约——用户确认——座位释放——取消预约成功
- 指纹登录——检查预约——开启成功
- 指纹注销——检查预约——关闭成功
- 管理员——修改权限
- 管理员——修改教室时间/座位
- 管理员——修改指纹/登录指纹
先写到这里,预约还没做完,等我寒假慢慢写~