由于这个系列文章是用等同于开发中AOP模式的方式在写, 所以一旦要涉及到具体项目开发的时候就会感觉出来混迟早要还的. 但是幸好, 完成一个项目, 写代码只不过是很小的一部分工作, 所以以下内容涉及到代码细节的部分, 我会用伪码将逻辑说明清楚, 如果你有兴趣正儿八经的把这个项目做出来的话, 只需要用自己拿手的语言和框架来把详细设计实施出来就行了.
项目的由来
老实说到处的例子都是做博客, BBS什么的, 既老套又无趣, 这个项目的来源是源于十几年前大学时候自己一个真实的想法, 但是最终因为各种原因而没有最终付诸实施, 所以这里当作一个示例项目, 用来详细的跟大家讲解一个项目如何从一个想法到最终成为大家使用的作品的完整过程.
共享书吧源于最朴素的网络共享主义思想, 免费, 共享. 大家把自己拥有的已经读过的实体书, 通过一个平台发布出来, 有需要的人, 只需要自己也发布一本书出来愿意供别人借去, 就可以去借自己需要的书. 你共享出来的书越多, 你能借到的书也越多.
需求分析: 分拆场景, 写User Story
什么是User Story?
User Story(使用者叙述)是一段简单的功能叙述, 以客户或者使用者的观点写下有价值的功能(functionality/feature). 与其说是规范文件, 不如说是代表客户的一个需求而已, 因为具体实施的细节会延迟到开发的时候才会确定.
User Story 第一版
我们从用户的角度来模拟一遍是如何使用这个设计都还没有的系统的. 我们把自己当作用户, 想象一下整个使用的过程:
U01: [用户] 需要 注册
U02: [用户] 需要登入系统才能操作
U03: [用户] 将自己的 [图书] 登记
U04: [用户] 登记的 [图书] 需要将其设置为可借出, 才能被搜索到
U05: [用户] 登记过 [图书] 后方可搜索别人登记并设为可借出的 [图书]
U06: [用户] 搜索到 [图书] 后对其所有者发出 [借书申请]
U07: [用户] 可以查看其他 [用户] 发出的 [借书申请]
U08: [用户] 在 [ 我收到的借书申请 ] 中 通过某用户的 [ 借书申请 ]
U09:[用户] 通过 [ 借书申请 ] 后将其设置为已经借出
U10: [用户] 收到 [图书] 后设置为已经到达
我在一边写的时候就将一些和系统的功能可能有关的词都括起来了, 以便接下来的分析.
我们先把User Stories里面所有的名词都抽取出来:
用户, 图书, 借书申请
然后找能和这些名词关联起来的其他词, 我们得到了下面的概念图:
另说到工具的问题, User Stories基本上手写卡片就OK了, 对, 就是很多高大上的外国开发团队的照片里满墙贴的便利贴上写的就是, 需求分析就是从满墙贴写User Story的便利贴开始的.上面的概念图呢其实也没用什么专门的工具, 就是KeyNote, 如果不用Mac的话PowerPoint其实也是不错的画图工具.别把时间浪费在找工具上, 用好手边的东西就能做好分析工作了.
根据User Stories 生成 UE模型
UE是从用户体验的角度构建模型-行为和UI之间的关联的一种描述工具,
做UE的工具很多, 但是最好的工具就是手边的纸和笔, 因为一旦上了工具, 思维就会受到工具的限制, 而且其实UE模型的根本使用文字来描述的, 图形只是方便直观的理解. 所以接下来就用手绘拍照的方式. 本人字丑, 多担待啦.
注册->登录
登录->我的图书
我的图书->添加图书
添加图书->待借出图书
待借出图书->我的借出申请
通过申请->寄出图书
上面的UE需要经过反复验证, 我们这里就跳过这个步骤了, 验证的目的在于两个点:
1) 验证有无逻辑上的断点, 比如状态推进到一些地方就走不通了.
2) 验证操作上有没有多余繁复的操作, 尽量简化, 同时也就是简化了用户交互的过程
因为跳过了验证过程, 所以上面我们第一稿的UE是有问题的, 主要是逻辑上不够全面, 有重复的地方, 所以留下一个问题, 由读者自行去完成第二版. 这也是一个锻炼产品能力的练习方法.
定稿后, 我们就可以通过UE来生成3个后续的生成物: UI/UX, 实体模型, 控制器结构
UI/UX 我们暂且跳过, 这个不在本系列讨论范畴, 一般网站出到UI就可以了, App的话最好是UI和UX都要出.
UE生成实体模型到数据表结构
基本的方式是, 对UE中, 和实体关联的名字都拿出来分析是不是属于实体的属性, 比如和用户关联的有 邮箱地址, 登录密码, 收货地址. 有一些属性是根据经验加入的, 比如用户的昵称, 头像, 还有状态
图书的属性在简化的线框UE并没有完全提现, 我们可以根据实际的数据来源来分析, 比如我们打算通过ISBN号直接从豆瓣抽取图书信息, 那么根据豆瓣的页面
根据上图我们可以抽取出图书的属性列表.
最后得到实体模型如下图:
根据之前的文章我们将用户拆分成两个表, 然后呢, 1-n, n-n 这些关系都需要中间关系表来支撑,最后我们得到了数据表结构如下图:
UE生成控制器结构
通过UE的我们可以将页面和按钮事件, 转化成action, 然后每个action对应到一个控制器, 这里对控制器我们用URL来表示.
注册/登录
我的图书/添加图书
待借出图书/搜索可借图书
我要借书/借书申请
寄出图书
设计进行到这一步已经相当的接近细节了, URL的设计风格不尽相同, 其实也没有强制的什么规范, 流行的Restful风格的URL设计更适合API, 而网页因为有的页面承载了多个功能, 所以在语义上并不是很契合, 这个问题我们后面会找个章节来详细说明一下.
剩下的工作就开始正式进入了写代码的阶段了. 下面的内容更多的偏向于小白了, 因为有过一定项目经验的同学已经可以通过上面的内容掌握如何开发一个Web项目了.
开发阶段
因为大家用的Web框架不一样, 所以开发阶段的细节都是不尽相同的, 所以我们这里抽象开发阶段的共性问题来讨论. 你可以将里所使用的Web框架和本文内容做比对, 或者将本文作为一个Guide来使用.
在实际的编码阶段, 我们就需要将前面所有的设计用代码的形式表达出来. 下面我们用伪码来详细说明一下每一个部分我们要做一些什么.
注册/登录
注册: /regist GET
直接返回静态的注册页面
点击注册按钮: /regist POST
1) 验证输入参数 email, password和address, 判断非空和email符合邮箱地址规范
2) 通过email在UserLogin表查询有无记录, 有记录返回错误: 该email已经注册了
3) hash密码后加盐二次hash生成存储的密码
4) UserLogin表插入记录, User表插入记录
5) 转跳页面到登录页
登录: /login GET
返回登录静态页
点击登录按钮: /login POST
1) 验证输入参数 email, password, 判断非空
2) 查询UserLogin表email等于输入参数email的记录, 为空就返回错误
3) userlogin记录的password字段里获取盐值和二次hash的值, 输入password进行hash后用盐值二次hash后和记录里的二次hash值对比, 不等则返回错误
4) 生成登录token写入cookie
5) 转跳到我的图书页面
我的图书: /books GET
查询UserBooks表条件是user_id等于登录用户的user_id, 获取到的记录用模板渲染图书列表后返回
添加图书: /books/new GET
返回添加数据的静态页面.
检测ISBN按钮: /books/isbn POST
1) 验证输入ISBN号(长度)
2) 在Book表中通过ISBN查询系统内有无该本图书, 如果有, 返回图书json
3) 通过豆瓣API, 用ISBN获取图书信息
4) 返回图书json
添加图书按钮: /books/new POST
1) 验证输入ISBN号(长度)
2) 在Book表中通过ISBN查询系统内没有该本图书
3) 通过豆瓣API, 用ISBN获取图书信息
4) 插入Book表
5) 在UserBooks表中查询用户有无这本书
6) 如果没有, 插入UserBooks表
待借出的书: /books/unborrow GET
在UserBooks获取用户处于未借出状态的图书, 通过模板渲染图书列表输出
设置为可借出: /book// PUT
1) 根据参数state为可借出就继续执行
2) 设置状态为可借出
设置为不可借出: /book// PUT
1) 根据参数state为不可借出就继续执行
2) 设置状态为不可借出
查询可借图书: /books/search POST
1) 检测参数key, 判断不能为空
2) 通过book表的属性, 模糊查询, 返回状态为可借出的图书
3) 通过模板渲染输出图书列表
我要借书按钮: /book// PUT
1) 检测输入属性state为提交申请就继续执行
2) 检测用户对该书有没有提交过借书申请
3) 设置状态为提交借书申请
4) 在Request表插入记录
我的借书申请: /books/borrow/request/me GET
在Request表用user_id等于登录用户user_id的条件查询登录用户发起的借书申请, 渲染图书列表返回
我收到的借书申请: /books/borrow/request/tome GET
在Request表join UserBooks表根据UserBooks表的user_id属性查询用户收到的的借书申请, 渲染图书列表返回
通过申请按钮: /book// PUT
1) 检测输入属性state为通过申请就继续执行
2) 设置图书状态为通过申请
待寄出图书列表: /books/unsended GET
在Request表里获取状态为待寄出的图书列表, 渲染图书列表返回
保存快递信息: /book// PUT
1) 检测输入属性里有sn就继续执行
2) 保存快递记录到Request表
设置为到货: /book// PUT
1) 检测输入属性state为到货
2) 将申请状态设置为到货
3) 将UserBooks的user_id修改为新的user_id
故事到了这里就进入尾声了, 你如果跟着一路做下来相信项目已经做得七七八八了, 但是你还有问题, 用户怎么修改密码呀? 怎么换头像啊? 怎么找回密码呀? 相信你跟全文走完全流程应该能自己补全剩下的部分了.
这个时候你已经搞定了第一版的程序, 接下来就可以上线运营了. 如果你运气很好, 用户量刷刷的上升, 那么很快你就会遇到新的问题了. 下一章我们继续这个故事
to be continue...