TIP

隔了这么多天都没有写博客,最近作业太多了,这个这次用的markdown格式感觉还可以。

# 1. 引言

# 1.1 选题背景

2020年,爆发了全球疫情新冠肺炎,由于有着较强传染性,大部分高校为了防止疫情进一步扩散,宣布本学期不开学,但是有少部分高校由于有科研任务,仍然被迫选择开学,造成了一定的风险性。学生到校,如何健康的生活,对于各个高校来说都是极具挑战性的,最好的办法就是隔绝,各个小卖铺、商店要求送货上门。

互联网的技术的飞快发展给人们的生活带来了极大的改变,以前为了买一个特殊的东西,说不定要跑上很远才可以买到,以前有“千里送鹅毛,礼轻情意重”,现在打开微信,就能见面聊天,然而我们却变得越来越懒了,吃饭有外卖,买东西专门有“跑腿”业务,催生了一系列的产业。在这个特殊时期,可以好好利用这个机会,发展用户。

# 1.2 选题意义

古代秀才不出门,尽知天下事,在网络发达的今天,作为普通人,也可以了解到比秀才还多的资源,给我们的生活带来了极大的便利。网络交换信息极速。市面上有类似的商城、外卖平台,可以说,已经做的非常好了,就是广告多,少发货量,也可以用微信代替,但是必须是好友,当存在大量订单的时候,就不便于管理了,不如大家同在一个平台,无广告,清爽,便捷。

# 2. 相关技术介绍

# 2.1 SSM框架技术

SSM(Spring+SpringMVC+MyBatis)框架集由Spring、MyBatis两个开源框架整合而成(SpringMVC是Spring中的部分内容)。常作为数据源较简单的web项目的框架。

Spring的两大核心思想是依赖注入(IOC)和面向切面编程(AOP),主要用于管理项目中的对象,简化开发流程。

SpringMVC包含了众多的“xxxx器”,视图解析器,前端控制器等等,在项目中用于拦截用户的请求,它的核心Servlet即DispatcherServlet承担中介或是前台这样的职责,将用户请求通过HandlerMapping去匹配Controller,Controller就是具体对应请求所执行的操作。

MyBatis是对jdbc的封装,它让数据库底层操作变的透明。MyBatis的操作都是围绕一个sqlSessionFactory实例展开的。MyBatis通过配置文件关联到各实体类的Mapper文件,Mapper文件中配置了每个类对数据库所需进行的sql语句映射。在每次与数据库交互时,通过sqlSessionFactory拿到一个sqlSession,再执行SQL命令。

页面发送请求给控制器,控制器调用业务层处理逻辑,逻辑层像持久层发送请求,持久层与数据库交互,后将结果返回给业务层,业务层将处理逻辑发送给控制器,控制器再通过视图解析器将页面渲染展现给用户。

# 2.2 MySQL数据库

MySQL数据库是一个关系型数据库管理系统,属于Oracle旗下产品,关系数据库将数据保存在不同的表中,使用SQL语言,体积小,速度快,最好的RDBMS应用软件之一。

# 2.3 Spring Security安全框架

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。与此功能相似的还有shiro安全框架,Spring Security对Web安全性的支持大量地依赖于Servlet过滤器,过滤器链拦截Servlet请求,并将这些请求转给认证和访问决策管理器处理,从而增强安全性。其有两大核心作用,身份认证与授权。

# 2.4 JSP和jQuery

JSP(全称JavaServer Pages)是由Sun Microsystems公司主导创建的一种动态网页技术标准。JSP部署于网络服务器上,可以响应客户端发送的请求,并根据请求内容动态地生成HTML、XML或其他格式文档的Web网页,然后返回给请求者。JSP技术以Java语言作为脚本语言,为用户的HTTP请求提供服务,并能与服务器上的其它Java程序共同处理复杂的业务需求。

jQuery是一个快速、简洁的JavaScript框架,它的设计的宗旨是“write Less,Do More”,即倡导写更少的代码,做更多的事情。它封装JavaScript常用的功能代码,提供一种简便的JavaScript设计模式,优化HTML文档操作、事件处理、动画设计和Ajax交互。

# 2.5 JSON

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写,同时也易于机器解析和生成。数据都是采用键值对的方式表示,任何支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等。但是对象和数组是比较特殊且常用的两种类型,对象用一组{}来表示,数组由[]表示。

# 3. 系统需求分析

# 3.1 系统可行性分析

  • 现实需求:现在疫情的爆发,人们都足不出户,学生到校也是要进行封闭管理的,为了减少人员的流动,提出了送货上门服务,这样的一个平台是能够带来便利的。

  • 技术可行性:由于现在网络速率的提高,微服务架构的优势日益凸显,市场上有着成熟的开发流程,项目后台基于Spring,SpringMVC,MyBatis搭建,数据库采用MySQL,安全框架使用Spring Security,密码使用Bcr加密,页面发送请求使用JSP和jQuery,数据交互格式采用JSON,将页面嵌入到Webview中。

  • 交易安全可行性:由于本系统只是涉及到类似于中间人身份,并不触碰现金,商家送上门之后再收取相关费用,从这方面看来是安全的。

# 3.2 系统需求分析

小卖铺系统的主要功能:

  • 商店老板可以在系统上架商品,如果是商店还没有入驻,需要注册,然后才可以发布。之后进入上架商品页面,填写商品属性,进行发布。发布成功后,将在系统页面展示。同样也可以下架商品。商品的属性有商品号,价格,名称,店铺号。

  • 学生首次使用需要注册,填写相关属性,然后登录,在平台中选择需要的商品,加入购物车,选择完毕,在购物车页面中进行下单操作,成功之后生成订单,然后系统给店铺老板发送通知。

  • 商店老板收到订单之后,检查货源是否充足,如果充足,就根据订单地址送货上门,如果不充足就拒绝发货。整个交易完毕。

# 4. 系统功能设计

# 4.1 系统总体设计

由上面的需求分析,设计出如下的上下文数据流图和0层数据流图。

学生
学生
老板
老板
小卖铺系统
小卖铺系统
通知
通知
上架/下架商品
上架/下架商品
查询订单/同意/拒绝发货
查询订单/同意/拒绝发货
选购/下单商品
选购/下单商品
变更订单
变更订单
身份信息
身份信息
身份信息
身份信息
图4-1 上下文数据流图
图4-1 上下文数据流图
Viewer does not support full SVG 1.1

将上面的上下文数据流图细化为0层数据流图,描述数据的走向。

学生
学生
商家
商家
学生表
学生表
商家表
商家表
商品列表
商品列表
购物车表
购物车表
订单列表
订单列表
1
检查是否登录
检查是否登录
4
购物车
购物车
6
下架商品
下架商品
3
选择商品
选择商品
8
变更订单
变更订单
5
上架商品
上架商品
1
检查是否登录
检查是否登录
9
生成订单
生成订单
选择商品
选择商品
登录
状况
登录 状况
未登录
未登录

登录
已 登录
加入商品
加入商品
删除商品
删除商品
下单
下单
记录订单
记录订单
通知
通知
检查
检查
7
确认订单
确认订单
备货
备货



送货上门...
获取
商品
信息
获取商品信息...
2
发货
发货
获取
地址
获取 地址
商品
商品
学生地址
学生地址
商家信息
商家信息
登陆状况
登陆状况
未登录
未登录
上架
上架
上架
上架
下架
下架
下架
下架
获取商品信息
获取商品信息
变更
变更
变更
变更
图4-2 0层数据流图
图4-2 0层数据流图
Viewer does not support full SVG 1.1

# 4.2 数据库分析与设计

根据数据流图中数据和实体对象进行数据库表的设计。

本项目涉及到学生、商家和管理员,归类于一个用户,只是不同的角色。基于角色的权限访问控制(RBAC),最低需要五个表,分别是用户、角色、权限、用户角色关系、角色权限关系表,此外在此项目中还需要要商品、订单、购物车表,分别做如下设计。

# 4.2.1 数据库ER图设计

根据实体对象的关系,做出如下的ER关系图。用户表包含着所有的用户,用角色关系表来体现不同的用户,权限表存储不同角色的操作权限。

用户
用户
关系1
关系1
用户号
用户号
角色
角色
权限
权限
商品
商品
关系2
关系2
购物车
购物车
类型
类型
密码
密码
角色号
角色号
角色名
角色名
角色描述
角色描述
创建时间
创建时间
权限号
权限号
权限名字
权限名字
权限标志
权限标志
地址
地址
订单
订单
创建时间
创建时间
总额
总额
图4-3 ER关系图
图4-3 ER关系图
Viewer does not support full SVG 1.1

# 4.2.2 数据库表的设计

(1) 订单表(orders),主键是id,自动递增,用户在下单的时候生成订单,订单表与用户表和商品表关联性较强,包括了买方与卖方,由订单表的状态给双方通知。

表 4-1 订单表
序号 列名 数据类型 主键 注释
1 user_id_boss int(11) 老板id
2 user_id_stu int(11) 学生id
3 date varchar(255) 创建时间
4 address varchar(255) 订单地址
5 price_sum double(10,2) 总额
6 good_id int(11) 商品id
7 flag int(11) 标志发货
8 id int(11) PRI 订单id

(2) 商品表(good),主键id递增,用于存储商品记录。

表 4-2 商品表
序号 列名 数据类型 主键 注释
1 id int(11) PRI 商品id
2 goodName varchar(50) 商品名
3 price double(10,2) 商品价格

(3) 权限表(permission),用于存储权限标识,主键id自动递增,permTag在后台用于标识权限信息。

表 4-3 权限表
序号 列名 数据类型 主键 注释
1 id int(11) PRI 权限id
2 permName varchar(50) 权限名字
3 permTag varchar(50) 权限标识

(4) 身份验证表(persistent_logins)是Spring Security自动生成的,当用户在登录界面中选中记住我,在这个表中就会产生一条记录,在token的有效期内用户的操作都是免登录的。

表 4-4 身份验证表
序号 列名 数据类型 主键 注释
1 username varchar(64) 用户名
2 series varchar(64) PRI 标识
3 token varchar(64) token令牌
4 last_used timestamp 最后使用

(5) 角色表(role)用于定义用户的角色,是学生,店长还是管理员,基于角色的权限访问控制(RBAC)的中间表,id是主键,自动递增。

表 4-5 角色表
序号 列名 数据类型 主键 注释
1 id int(11) PRI 角色id
2 roleName varchar(50) 角色名
3 roleDesc varchar(255) 角色描述

(6) 用户权限关系表(role_permission)用户建立角色和权限的关系,角色和权限是一个多对多的关系,一个角色可以拥有多个权限,一个权限也可以被多个用户拥有。

表 4-6 角色和权限关系表
序号 列名 数据类型 主键 注释
1 role_id int(11) PRI 角色id
2 permission_id int(11) PRI 权限id

(7) 购物车表(shopcart)是专给学生(买家)用的,将商品添加到购物车,与商品建立关系。

表 4-7 购物车表
序号 列名 数据类型 主键 注释
1 user_id int(11) PRI 用户id
2 good_id int(11) PRI 商品id

(8) 用户表(user)用于存储用户信息,主键id,自动递增,由于运用了安全框架,可以设置用户的一些状态,提高安全性。

表 4-8 用户表
序号 列名 数据类型 主键 注释
1 id int(11) PRI 用户id
2 username varchar(50) 用户名
3 realname varchar(50) 真实姓名
4 password varchar(255) 密码
5 createDate date 创建时间
6 lastLoginTime date 最后登录时间
7 enabled int(5) 是否可用
8 accountNonExpired int(5) 账户未过期
9 accountNonLocked int(5) 账户未锁定
10 credentialsNonExpired int(5) 凭据未过期
11 address varchar(255) 地址

(9) 用户商品关系表(user_good)用户建立用户和商品的关系,和购物车表一样,但是意义不同,购物车表表示的是学生与商品的关系,用户商品关系表表示的是店长和商品的关系。

表 4-9 用户商品关系表
序号 列名 数据类型 主键 注释
1 user_id int(11) PRI 用户id
2 good_id int(11) PRI 商品id

(10) 用户角色关系表(user_role)用于建立用户与角色的关系,用户要想有权限,就必须是一种角色,然后对角色进行授权,间接用户就有权限了。

表 4-10 用户角色关系表
序号 列名 数据类型 主键 注释
1 user_id int(11) PRI 用户id
2 role_id int(11) PRI 角色id

# 4.3 系统模块详细设计

# 4.3.1 用户登录

用户登录验证采用的是Spring Security自带的一个表单登录,重新定制登录页面,集成图片验证码,防止恶意用户,可根据自己需求选择记住我,一个小时免登录。其实在后端的过滤器链首先验证的是,验证码是否正确,然后才是用户名密码,两个同时正确,身份验证通过。

开始
开始
结束
结束
主页
主页
有账号?
有账号?
输入用户名密码
输入用户名密码
true?
true?
输入图片验证码
输入图片验证码
true?
true?
登录成功
登录成功
N
N
N
N
注册
注册
N
N
Y
Y
Y
Y
Y
Y
图4-4 用户登录流程
图4-4 用户登录流程
Viewer does not support full SVG 1.1

# 4.3.2 生成订单

生成订单,是学生的功能,首先需要将首页展现的商品列表加入到购物车中,对于前台来说,需要向后端购物车接口发送商品id和用户id,建立联系。然后学生进入到购物车页面,选中加入到购物车中的商品,点击下单,这是时候,会向后端生成订单接口发送三个参数,分别是学生id,店长id,商品id,这样后端的业务代码就会在订单表中生成一条新的记录。

图4-5 生成订单流程
图4-5 生成订单流程
Viewer does not support full SVG 1.1

# 4.4 页面交互流程

用户分为三个角色,店长,学生,管理员,不同的角色具有不同的功能。

在首页中,展现了一个商品列表,以及登录和注册的按钮。没有账户的用户进行注册,有账户的进行登录,因为进行了用户角色分类,操作的界面显示都不太一样。

对于学生来说,可以修改个人信息,将首页的商品加入到购物车中,对以添加到购物车中商品下单,查看订单的状况,是否已经发货。

管理员权限原则上是无限大的,任何用户可以进行的操作,管理员都可以,但是这是无意义的,试想,一个管理员可以发货又可以下单,这貌似也行,咸鱼APP上就是如此,但是限于开发时间,现只做了用户管理和商品管理。对于违规的商品可以删除,同时也可以添加用户,更改用户信息。

主页
主页
注册
注册
登录
登录
学生
学生
管理员
管理员
店长
店长
我的订单
我的订单
加入购物车
加入购物车
购物车
购物车
退出
退出
我的信息
我的信息
修改地址
修改地址
修改密码
修改密码
下单
下单
删除
删除
退出
退出
我的订单
我的订单
店长商品管理
店长商品管理
我的信息
我的信息
修改地址
修改地址
修改密码
修改密码
学生
学生
商品上架
商品上架
商品删除
商品删除
商品管理
商品管理
退出
退出
用户管理
用户管理
删除商品
删除商品
添加用户
添加用户
更改用户信息
更改用户信息
图4-6 页面交互流程图
图4-6 页面交互流程图
Viewer does not support full SVG 1.1

# 5. 系统实现

# 5.1 开发环境

表5-1 开发环境信息表
种类 名称 版本
开发工具 IDEA 2020.1
开发操作系统 win10 家庭版1903
数据存储 MySQL 5.5
开发语言 JAVA jdk8
服务器 Tomcat 9.0.30
云服务器 Ubuntu 18.04
安卓开发工具 Android Studio 3.4.1

# 5.2 匿名用户首页

安卓客户端首次进入就是主页,主要是做数据展示以及用户登录和注册,匿名用户首先需注册或者登录,方可进行下一步操作。

图5-1 匿名用户首页
图5-1 匿名用户首页
Viewer does not support full SVG 1.1

# 5.3 用户登录界面展示

用户登录界面,需要输入用户名,密码,验证码,进行身份验证,验证次序如下,首先进行验证码验证,如果验证码输入错误,则会弹出“验证码不一致”弹窗,验证码正确后,再验证用户名和密码,如果输入有误就会弹出“Bad credentials”错误,两个都验证成功后,进入进入主页,Spring Security会验证用户的角色,展现不同的功能。

图5-2 用户登录界面
图5-2 用户登录界面
Viewer does not support full SVG 1.1

# 5.4 学生首页实现

学生登录成功,拥有如下功能,查看和修改我的信息,查看我的订单,再购物车中下单,以及选中商品列表中的商品,加入到购物车中。

图5-3 学生首页
图5-3 学生首页
Viewer does not support full SVG 1.1

# 5.5 店长商品上架

店长可以管理自己上架的商品,输入商品的名字,以及价格,点击提交,就能成功地将商品上架,对于后台数据库来说,商品表将增加一条记录,商品与用户地关系表也要增加一条记录,维持关系。同样的,店长也可以对自己的商品进行删除操作,选中要删除的商品,然后点击删除即可。

图5-4 店长商品管理页面
图5-4 店长商品管理页面
Viewer does not support full SVG 1.1

# 5.6 管理员用户管理

管理员可以对用户进行管理,首先管理员可以添加用户,可以选择用户的类型,这里增加用户(注册用户),考虑还是有些不足的,输入相同的用户名,注册虽然失败,但是还是提示注册成功,用户名用于唯一标识一个用户。考虑到可能某些用户密码会忘记,管理员能进行密码修改,同时在这个页面中也展示了所有注册的用户。

图5-5 管理员用户管理界面
图5-5 管理员用户管理界面
Viewer does not support full SVG 1.1

# 6. 测试与验证

# 6.1 用户登录测试

再首页点击登录按钮,进入登录页面,输入用户名,密码,验证码,进行功能测试。

表 6-1 用户登录测试表
过程 预期结果 测试结果 预设条件
不输入任何数据 提示“输入验证码不一致” Y
用户名或者密码错误 提示“Bad credentials” Y
验证码输入错误 提示“输入验证码不一致” Y
用户名:hexo,密码:123456,验证码填写正确 登录成功返回到主页 Y

# 6.2 学生下单

学生登录之后,将首页的商品选择后添加到购物车,然后在进入到购物车页面,选择要购买的商品,点击下单,即生成了订单,在订单页面立即出现订单的状态发货还是未发货,对于店长来说,将会收到这条通知,然后对订单进行发货操作。

表 6-2 学生下单测试表
过程 预期结果 测试结果 预设条件
不选择任何商品点击下单 订单生成失败 N
选择一个商品进行下单 生成一条订单 Y
选择多个商品下单 生成多条订单 Y

# 6.3 商品上架

商品上架是店长的功能,可以对商品进行管理,商品,主要有两个参数,商品名,商品价格,数据库中的商品表还有一个商品id,唯一标识一个商品。

表 6-3 商品上架测试表
过程 预期结果 测试结果 预设条件
不填写任何商品信息提交表单 提醒未填写 N
填写商品名,但是不填写价格 提醒未填写价格 N
填写价格,不填写商品名 提醒未填写商品名 N
两个都填写 商品添加成功 Y

经测试,发现商品信息未填写完全,也是可以显示商品提交成功的,但是实际上后台不会添加这个商品,设置输入框必填,解决了这个问题。

# 6.4 管理员更改用户密码

用户管理是管理员的功能,包括对于用户的增加,和更改用户密码,考虑到管理员删除用户不友好的,另外用户的个人信息,管理员也不应修改,当用户忘了密码时可以寻找管理员重置密码。

表 6-4 管理员更改用户密码测试表
过程 预期结果 测试结果 预设条件
不输入用户名 请输入用户名 N
输入用户名,不输入密码 请输入密码 N
不输入用户名,输入密码 请输入用户名 N
输入用户名和密码 更改密码成功 Y

经测试,发现什么输入也会显示密码修改成功,这个显然是不行的,为input设置了`required`属性,但是由于不是from表单形式,也就自然无效了。

# 7. 收获

由于最近在学web后端,所以就想着能不能做一款网络应用,起初也不知道需要做些什么,简单的,对于数据库的一些增删改,也不想做,因为有了SSM和SpringBoot,做出API响应接口是十分容易的。这一次做下来,发现最重要的就是数据如何的存储,这次做的也存在着很多的毛病,例如如何唯一标识一个商品,如果设置id为唯一标识,自动自增,如果不涉及到商品的删除还好点,假如某个商品删除,对于商品的增删改可能会有问题。数据的交互没有搞好,js和jsp数据如何互相使用,过多的依耐它们两个所拿到的值,js可以判断你选中了哪些组件,但是却并不知道有多少个,需要jsp的配合使用,总之很麻烦。

还有就是本次使用了Spring Security进行身份认证与授权,它可以很好地对于用户的一些权限进行控制,可以设置哪些接口被那些角色所访问,它还有在页面中使用它的标签库,在页面中,使用``包含的内容可以控制内容的一个展示,用户包括权限,则展示相应的功能和内容。

也奋斗好几天,没日没夜的,我觉得收获还是挺大的,网上看别人做的很好,就算一步步跟着也是可能会出错的,可能会花上数个小时,翻阅数篇博客,我发现用的最多的时间不是编代码,而是解决bug,这就是个经验累积的过程吧,以前在服务器上部署个web坏境,可能会花上一天甚至还未解决,现在大概十几分钟就行了。踩坑的过程就是学习的过程。但是不知道为什么,服务端程序部署到云tomcat服务器中不能运行,又掉坑去了。

# 8. 附录

# AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.zuoye">
    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:allowBackup="true"
        android:usesCleartextTraffic="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

# activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <WebView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/web_view"/>
</LinearLayout>

# MainActivity.java

package com.example.zuoye;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Build;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       WebView main_webview = findViewById(R.id.web_view);
        WebSettings settings = main_webview.getSettings();
        settings.setJavaScriptCanOpenWindowsAutomatically(true);//设置js可以直接打开窗口,如window.open(),默认为false
        settings.setJavaScriptEnabled(true);//是否允许执行js,默认为false。设置true时,会提醒可能造成XSS漏洞
        settings.setSupportZoom(true);//是否可以缩放,默认true
        settings.setBuiltInZoomControls(true);//是否显示缩放按钮,默认false
        settings.setUseWideViewPort(true);//设置此属性,可任意比例缩放。大视图模式
        settings.setLoadWithOverviewMode(true);//和setUseWideViewPort(true)一起解决网页自适应问题
        settings.setAppCacheEnabled(true);//是否使用缓存
        settings.setDomStorageEnabled(true);//DOM Storage
        main_webview.loadUrl("http://10.0.2.2:8080/shop2_war/");
        //该方法解决的问题是打开浏览器不调用系统浏览器,直接用webview打开
        main_webview.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
        });
    }
}