Compare commits

...

10 Commits

@ -1,2 +1,493 @@
# Used-Trading-Platform.txt
# 基于SSM的校园二手交易平台
**完整的设计报告在后面**
视频教程:<br>
https://www.bilibili.com/video/BV1jc411H7aX/
喜欢就点一下star哟谢谢亲的支持
https://github.com/wsk1103/Used-Trading-Platform2
个人主页
https://wsk1103.github.io/
使用 Spring Boot 一键式启动。
下载项目运行c2c.sql文件然后运行 UsedTradingPlatformApplication.main 方法即可启动。
环境
jdk1.8
mysql5.7
先在数据中创建名为c2c的数据库使用Navicat for MySQL运行 c2c.sql文件创建表和导入数据
## 3.2 实现过程
### 3.2.1 商品首页实现
![这里写图片描述](https://img-blog.csdn.net/20180506142520732?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
![这里写图片描述](https://img-blog.csdn.net/20180506142530664?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
其他部分实现效果
![这里写图片描述](https://img-blog.csdn.net/20180506142550165?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
![这里写图片描述](https://img-blog.csdn.net/20180506142617150?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.2 二手商城实现
![这里写图片描述](https://img-blog.csdn.net/2018050614262516?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
商城商品展示
![这里写图片描述](https://img-blog.csdn.net/20180506142641307?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
![这里写图片描述](https://img-blog.csdn.net/20180506142650930?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
![这里写图片描述](https://img-blog.csdn.net/20180506142655874?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.3 商品详情实现
![这里写图片描述](https://img-blog.csdn.net/20180506142702750?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
留言
![这里写图片描述](https://img-blog.csdn.net/20180506142707609?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.4 站内搜索引擎实现
![这里写图片描述](https://img-blog.csdn.net/20180506142713316?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
搜索结果
![这里写图片描述](https://img-blog.csdn.net/20180506142717344?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.5 发布商品实现
![这里写图片描述](https://img-blog.csdn.net/20180506142725661?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
![这里写图片描述](https://img-blog.csdn.net/20180506142737493?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
![这里写图片描述](https://img-blog.csdn.net/20180506142751872?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.6 发布求购信息实现
![这里写图片描述](https://img-blog.csdn.net/20180506142803686?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.7 个人信息页面实现
![这里写图片描述](https://img-blog.csdn.net/20180506142810540?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
![这里写图片描述](https://img-blog.csdn.net/20180506142815483?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.8 我发布的商品实现
![这里写图片描述](https://img-blog.csdn.net/20180506142820866?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
点击修改
![这里写图片描述](https://img-blog.csdn.net/2018050614282592?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.9我发布的求购信息实现
![这里写图片描述](https://img-blog.csdn.net/2018050614284365?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
![这里写图片描述](https://img-blog.csdn.net/20180506142847753?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.10购物车实现
![这里写图片描述](https://img-blog.csdn.net/20180506142855913?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
点击添加数量和减少
![这里写图片描述](https://img-blog.csdn.net/20180506142901134?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.11登录注册实现
![这里写图片描述](https://img-blog.csdn.net/20180506142908919?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
![这里写图片描述](https://img-blog.csdn.net/20180506142916295?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
![这里写图片描述](https://img-blog.csdn.net/20180506142923392?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
# 一、设计概要
  本次设计的是一个校园二手交易平台C2CC2C指个人与个人之间的电子商务买家可以查看所有卖家发布的商品并且根据分类进行商品过滤也可以根据站内搜索引擎进行商品的查询并且与卖家联系达成交易的意向也可以发布求购的信息让卖家查看之后与之联系进行交易。而此次主要是针对校园用户所设计的网站对于数据的分类应该更多的考虑校园用户的需求例如二手书籍、二手数码等的分类应该更加细致。本次设计的主要难度在于数据的详细分类对于数据的过滤必须要严谨应当考虑敏感字体和敏感图片的过滤。
  可能遇到的问题有在数据交互过程中出现由于属性名的差距导致的数据交互失败。由于Css属性设置的问题导致界面不完整等等。设计的算法都是自己编写的可能存在不是最优算法的情况。
# 二、设计正文
## 1 需求分析
  建立一个用户可以自由交易的平台通过ajax实现局部刷新实现网站更具人性化具有更良好的互动。以下是总体需求
### 1.1 通过手机号码注册账号并且登陆
  每个手机号码只可以注册一个账号,并且通过账号完善个人信息和浏览商品,发布商品等,注册时需要通过手机号码获取验证码进行验证才能注册。
### 1.2 实现二手商城
  商城主要显示总的商品信息,并且可以通过侧边栏点击进行信息过滤。商品点击之后可以查看详细信息。
### 1.3 站内搜索引擎
  实现本站内特有的搜索引擎,可以输入字段,模糊查询合适的商品并且显示出来。
### 1.4 求购信息发布
  根据需求输入相应的信息,发布求购商品。
### 1.5 求购商城
  卖东西的用户,也可以通过求购商城查看是否有用户对自己拥有的二手物品有需求,查看详细的需求,有的话可以跟买家联系进行交易。
### 1.6 货物出售信息发布
  卖家可以发布二手商品的信息,对商品进行描述,并且添加图片增加可信度,也方便买家查看相应的信息。
### 1.7 购物车
  将想要的物品添加到购物车,可以修改数量,选择是否要支付,移除商品。选择收货地址,并且进行结算。
### 1.8 个人信息
  用户发表求购信息和发布商品时需要先进行信息的完善,用户可以查看自己发布的商品,对发布的商品进行修改或删除,查看已发布求购信息,对求购的商品进行修改或删除。
## 2 概要设计
  系统的整体结构流程图如下
![这里写图片描述](https://img-blog.csdn.net/20180506142214677?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 2.1 系统模块
  本系统的可以分为以下几个模块。
#### 2.1.1 商品首页
  负责显示热门的商品信息以及显示本网站的网站信息导航栏负责跳转到各个页面没有登录显示按钮可以让用户进行登陆和注册。已登录的用户显示用户名并且可以发布商品信息查看个人信息等。进入首页的时候通过Ajax获取数据库中存在的热门商品数据集合并且刷新页面的内容点击商品之后跳转到商品详细信息模块。
#### 2.1.2 二手商城模块
  负责显示所有的二手商品提供类别的侧边栏给用户点击之后显示对应的商品信息。类别侧边栏的实现后需要为每一个类别的元素都添加一个id通过id来获取数据库中的数据用Ajax获取数据并且刷新页面的信息。采用分页技术防止数据过多的时候显示在同一个页面给用户带来不好的体验分页支持点击下一页上一页或者直接点击指定页面跳转到指定的页面。商品点击之后就要跳转到商品详情。
#### 2.1.3 商品详情模块
  负责显示指定的商品详细信息包括图片、名字、价格、数量等当一个商品被点击之后通过返回的id查询到这个数据集合跳转到商品详细信息的页面商品发布者可以在下方查看留言并且与有意者打成交易意向。将对应的信息显示出来并且提供加入到购物车的功能。商品详细信息下方显示其他用户的留言并且已经登录的用户可以对这件商品进行评论商品发布者可以在下方查看留言并且与有意者打成交易意向。商品发布者可以在下方查看留言并且与有意者打成交易意向。
#### 2.1.4 站内搜索引擎
  每一个页面顶部都存在一个搜索输入框,用户通过输入模糊的商品信息,后台数据库通过查询过滤相关的商品信息,并且显示出来给用户查看,显示出来的商品点击之后可以显示商品的详细信息。
#### 2.1.5 发布商品
  用户输入指定的信息以及选择商品的图片之后将这些信息结合当前登陆的用户通过Ajax发表到后台并且通过框架存储数据到数据库指定表。
#### 2.1.6 发布求购信息
  用户输入指定的信息以及选择商品的图片之后将这些信息结合当前登陆的用户通过Ajax发表到后台并且通过框架存储数据到数据库指定的求购信息表。
#### 2.1.7 个人信息模块
  显示个人信息例如用户名、真实姓名、宿舍号、学号等显示之后还需要支持对于数据进行修改修改之后要同步修改页面的信息这需要用到Ajax进行数据的提交并且进行页面的局部刷新。
#### 2.1.8 我发布的商品模块
  显示个人发表的商品信息,支持点击之后对数据进行修改,例如修改商品名字,修改商品的单价和数量等,修改之后将新的数据提交到数据库,数据库执行相应的修改操作,也可以直接下架某件商品,点击删除按钮之后,直接在后台数据库删除这一件商品,并且刷新当前页面上显示的个人发布的商品。给用户带来更好的体验效果。
#### 2.1.9 我发布的求购信息模块
  显示个人发表的求购商品信息,支持点击之后对数据进行修改,例如修改商品名字,修改商品的单价和数量等,修改之后将新的数据提交到数据库,数据库执行相应的修改操作,也可以直接下架某件商品,点击删除按钮之后,直接在后台数据库删除这一件商品,并且刷新当前页面上显示的个人求购的商品。
#### 2.1.10 购物车模块
  显示用户加入购物车的商品计算总的价格提供全选和取消全选的按钮从后台获取商品的单价判断当前剩余的数量当用户点击数量添加或者减少之后对于总价要实时刷新采用jQuery对数据进行修改当用户取消选中其中的某一个商品要相应的减少价格并且刷新提供选择收货地址对收获地址进行管理选好收货地址之后结算购物车弹出支付页面让用户选择付款方式并且提交。
#### 2.1.11 登录注册模块
  用户通过手机号码和密码进行登录登录之后显示首页。当用户没有账号的时候提示用户进行注册从登录界面切换到注册界面注册需要通过手机号码获取验证码后台通过页面传递的手机号码随机生成4位数的验证码并且缓存之后通过发送139邮箱的方式发送到指定的手机手机收取到验证码之后输入验证码提交判断是否正确正确则注册成功失败则注册失败。用户注册完之后直接跳转到首页。
## 2.2 数据结构设计
  首页需要的数据是热门商品的信息商品信息需要建立一个Bean对象存储内容是商品的id、商品名称、商品单价、商品类别、商品数量、商品详细信息、商品成色、商品附带的图片地址、热门程度、发表用户的id、留言的集合当用户发表之后保存数据到Bean对象中并将它存储到数据库。首页请求数据之后根据热门度返回多个数据将数据存储到list集合中并且将它转换为JSON格式数据返回到页面页面解析数据并且显示。
  商场则负责显示所有的商品数据实现侧边栏点击指定的类别之后显示对应类别的数据需要的是类别的id因此类别Bean对象的数据是类别id、类别名称点击之后通过类别id获取到商品详情集合并且将它转为JSON格式的数据返回到前端页面显示数据。
  点击商品之后需要显示商品详细信息通过商品的id返回指定商品的Bean对象转化为JSON格式的数据返回到前端页面显示需要显示商品的留言信息因此需要留言的Bean对象包括数据留言id、留言的商品id、留言内容、留言时间结合商品的详情返回JSON格式数据前端解析之后显示。
  求购商城负责显示所有的求购商品需要求购商品的Bean内容包括求购商品id、求购商品名称、求购商品数量、求购商品单价、求购商品详细信息、求购商品留言集合。页面显示时从后台返回对象集合转变的JSON数据前端解析数据并且显示出来。
  登陆界面需要验证用户信息用户信息包括内容用户id、用户名、用户的手机号码、用户密码、用户真实姓名、用户地址、用户email、用户性别、用户学号。验证的时候只需要判断手机号码和密码是否符合符合则验证通过返回验证结果用JSON存储数据。失败则返回失败信息。
  主要用到的数据结构是ArrayList负责组合各种信息并且转换为JSON格式数据返回。数据需要排序的时候用到的是快速排序负责排列热门商品等的信息并且返回数据到前端页面进行显示。
# 3 详细设计
## 3.1 数据字典
用户信息表
| 字段名 | 字段类型| 是否可为空| 备注|
|:------:|:------:|:------:|:------:|
| Id| Int(11) | 否| 主键|
|Modify |Datetime| 是| 修改时间|
|Username| Varchar(50) | 否| 用户昵称|
|Phone| Char(11)| 否 | 用户手机号码|
|realName| Varchar(20) | 是| 用户真实姓名|
|Clazz| Varchar(20) |是| 用户所在班级|
|Sno| Char(12)| 是| 用户学号|
|Dormitory| Varchar(20) | 是| 宿舍号|
|Gender| Char2| 是| 性别|
|Createtime |Datetime| 是| 创建时间|
|Avatar| Varchar(200)| 是 | 头像|
用户密码表
|字段名 |字段类型| 是否可为空| 备注|
|:------:|:------:|:------:|:------:|
|Id| Int | 否| 主键|
|Modify |Datetime| 是 |修改时间|
|Password| Varchar(24) | 否| 用户密码|
|Uid| Int| 否| 用户id|
商品表
|字段名 |字段类型| 是否可为空| 备注|
|:------:|:------:|:------:|:------:|
| Id| Int(11) | 否| 主键
|Modify| Datetime| 是| 修改时间
|Name| Varchar(50) | 否| 商品名称
|Level| Int |否| 商品成色
|Remark |Varchar(255)| 是| 商品详细信息
|Price| Decimal(0.00)| 否| 商品价格
|Sort| Int | 否| 商品类别
|Count| Int | 否| 商品数量
|Display| Int | 否| 商品是否被下架
|Transaction |Int| 否 | 交易方式
|Sales| Int | 否| 商品销量|
|Uid |Int | 否| 发表的用户id|
|Image| Varchar(255)| 否| 商品的图片地址
商品留言
|字段名 |字段类型| 是否可为空| 备注
|:------:|:------:|:------:|:------:|
| Id| Int(11)| 否| 主键
|Modify| Datetime| 是| 修改时间
|Sid| Int | 否| 商品的id
|Content| Varchar(122)| 否| 留言内容
|Display |Int| 否| 是否可见
|Uid |Int | 否| 评论的用户id
我的收藏
|字段名 |字段类型| 是否可为空| 备注
|:------:|:------:|:------:|:------:|
| Id| Int(11) | 否| 主键
|Modify| Datetime| 是| 修改时间
|Sid |Int | 否 |商品的id
|Display |Int| 否| 是否可见
|Uid |Int | 否| 评论的用户id
求购商品
|字段名| 字段类型| 是否可为空| 备注
|:------:|:------:|:------:|:------:|
| Id| Int(11) | 否| 主键
|Modify| Datetime| 是| 修改时间
|Name| Varchar(50) |否| 求购商品名称
|Remark| Varchar(255)| 是| 求购商品详情
|Price| Decimal(0.00)| 否| 求购商品价格
|Sort| Int | 否| 求购商品类别
|Count| Int | 否| 求购商品数量
|Display| Int | 否| 信息是否被删除
|Transaction| Int | 否| 交易方式
|Uid |Int | 否| 发表的用户id
|Image| Varchar(255)| 否| 商品的图片地址
求购商品留言
|字段名| 字段类型| 是否可为空| 备注
|:------:|:------:|:------:|:------:|
| Id| Int(11) | 否| 主键
|Modify| Datetime| 是| 修改时间
|Qid |Int | 否| 求购商品的id
|Content| Varchar(122)| 否| 留言内容
|Display| Int| 否| 是否可见
|Uid |Int | 否| 评论的用户id
我发布的商品
|字段名| 字段类型| 是否可为空| 备注
|:------:|:------:|:------:|:------:|
| Id| Int(11) | 否| 主键
|Modify| Datetime| 是| 修改时间
|Display |Int | 否| 是否被删除
|Uid |Int | 否| 用户id
|Sid |Int| 否| 对应商品id
我求购的商品
|字段名| 字段类型| 是否可为空| 备注
|:------:|:------:|:------:|:------:|
| Id| Int(11) | 否| 主键
|Modify| Datetime| 是| 修改时间
|Display| Int | 否| 是否被删除
|Uid| Int | 否| 用户id
|Qid| Int | 否 |求购商品id
已购买的商品
|字段名| 字段类型| 是否可为空| 备注
|:------:|:------:|:------:|:------:|
| Id| Int(11) | 否 |主键
|Modify| Datetime| 是| 修改时间
|State| Int | 否| 商品当前的状态
|Uid |Int | 否| 用户id
|Sid| Int | 否| 商品id
|Quantity| Int | 否| 商品数量
购物车
|字段名| 字段类型| 是否可为空| 备注
|:------:|:------:|:------:|:------:|
| Id| Int(11) | 否| 主键
|Modify| Datetime | 是| 修改时间
|Display| Int| 否| 商品是否被删除
|Uid| Int| 否| 用户id
|Sid| Int| 否| 商品id
|Quantity| Int | 否| 商品数量
## 3.2 实现过程
### 3.2.1 商品首页实现
  负责显示热门的商品信息以及显示本网站的网站信息导航栏负责跳转到各个页面没有登录显示按钮可以让用户进行登陆和注册。已登录的用户显示用户名并且可以发布商品信息查看个人信息等。进入首页的时候通过Ajax获取数据库中存在的热门商品数据集合并且刷新页面的内容点击商品之后跳转到商品详细信息模块。
  首页的前端设计大体如下
  顶部实现导航栏导航栏包括网站logo支持点击之后返回到首页、输入框和按钮负责站内搜索引擎的实现、以及一个span包裹的数据当有用户登陆时将它替换为用户名没有用户登录时则将它设置为登录注册按钮方便用户进行登录和注册。用户登陆之后显示出来的用户名可以通过绑定鼠标进入事件弹出选择框让用户选择相应的操作例如显示个人信息、查看发布的商品、查看求购的商品、发布商品、发布求购商品信息。导航栏与其他页面共用。
![这里写图片描述](https://img-blog.csdn.net/20180506142520732?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
  显示热门商品信息采用轮播图实现主要是先定义一个顶级的div指定position是绝对位置将除了当前显示的页面之外的轮播图div都隐藏起来为当前的div添加一个current类判断点击的方向如果是右则先获取到current的下一个div并且将它准备在右侧等候进入当前的div移出之后就将下一个进入并且删除原来的current类将它添加给新的div。在此过程中控制动画就可以实现一个精美的轮播图。
![这里写图片描述](https://img-blog.csdn.net/20180506142530664?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
  其他部分实现效果
![这里写图片描述](https://img-blog.csdn.net/20180506142550165?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
![这里写图片描述](https://img-blog.csdn.net/20180506142617150?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.2 二手商城实现
  负责显示所有的二手商品提供类别的侧边栏给用户点击之后显示对应的商品信息。类别侧边栏的实现后需要为每一个类别的元素都添加一个id通过id来获取数据库中的数据用Ajax获取数据并且刷新页面的信息。采用分页技术防止数据过多的时候显示在同一个页面给用户带来不好的体验分页支持点击下一页上一页或者直接点击指定页面跳转到指定的页面。商品点击之后就要跳转到商品详情。
  二手商城前端实现
  主要是设计侧边栏比较有难度,首先获取后台返回的三级类别,并且将顶级类别显示在侧边栏中,当鼠标移动上去的时候,显示其余两级目录,显示次级目录作为标题,而第三级的目录则是可点击的按钮,点击之后显示相应类别的商品信息。
![这里写图片描述](https://img-blog.csdn.net/2018050614262516?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
商城商品展示
![这里写图片描述](https://img-blog.csdn.net/20180506142641307?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
  其次是底部分页按钮的实现左右切换需要通过jQuery来控制下一页则判断是否已经到达当前显示的页数倒数第二个如果是就删除第一个页数并且添加下一页的页数前提是还有页数可以继续如果不是倒数第二个就将当前元素的current类名删除并将它赋给下一个元素。
![这里写图片描述](https://img-blog.csdn.net/20180506142650930?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
![这里写图片描述](https://img-blog.csdn.net/20180506142655874?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.3 商品详情实现
  负责显示指定的商品详细信息包括图片、名字、价格、数量等当一个商品被点击之后通过返回的id查询到这个数据集合跳转到商品详细信息的页面商品发布者可以在下方查看留言并且与有意者打成交易意向。将对应的信息显示出来并且提供加入到购物车的功能。商品详细信息下方显示其他用户的留言并且已经登录的用户可以对这件商品进行评论商品发布者可以在下方查看留言并且与有意者打成交易意向。商品发布者可以在下方查看留言并且与有意者打成交易意向。
  显示商品的详细信息并且添加可以留言的模块用户发表留言之后会在下方的留言板通过ajax进行实时的刷新。
![这里写图片描述](https://img-blog.csdn.net/20180506142702750?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
  留言
![这里写图片描述](https://img-blog.csdn.net/20180506142707609?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.4 站内搜索引擎实现
  每一个页面顶部都存在一个搜索输入框,用户通过输入模糊的商品信息,后台数据库通过查询过滤相关的商品信息,并且显示出来给用户查看,显示出来的商品点击之后可以显示商品的详细信息。
![这里写图片描述](https://img-blog.csdn.net/20180506142713316?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
  搜索结果
![这里写图片描述](https://img-blog.csdn.net/20180506142717344?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.5 发布商品实现
  用户输入指定的信息以及选择商品的图片之后将这些信息结合当前登陆的用户通过Ajax发表到后台并且通过框架存储数据到数据库指定表。
  三级联动的实现用Ajax获取到三级的类别JSON数据之后解析JSON数据将第一级的添加到第一个下拉框第二级的添加到第二个下拉框第三级的添加到第三个下拉框当第一个或者是第二个发生改变的时候通过获取父容器的子集合修改后两级或者一级的下拉框内容用jQuery的html替换即可。
![这里写图片描述](https://img-blog.csdn.net/20180506142725661?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
![这里写图片描述](https://img-blog.csdn.net/20180506142737493?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
 点击图片后显示缩略图需要先采用一个file的input获取到file的文件集合并且获取它的网页缓存地址获取后将它设置到img的src中可以添加一个渐变的动画效果给用户带来更好的视觉体验。
![这里写图片描述](https://img-blog.csdn.net/20180506142751872?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.6 发布求购信息实现
 用户输入指定的信息以及选择商品的图片之后将这些信息结合当前登陆的用户通过Ajax发表到后台并且通过框架存储数据到数据库指定的求购信息表。三级联动采用前一个的即可。
![这里写图片描述](https://img-blog.csdn.net/20180506142803686?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.7 个人信息页面实现
  显示个人信息例如用户名、真实姓名、宿舍号、学号等显示之后还需要支持对于数据进行修改修改之后要同步修改页面的信息这需要用到Ajax进行数据的提交并且进行页面的局部刷新。
  所有信息采用jQuery的EasyUI中的accordion显示标题显示信息而底部内容则显示修改的组件之后Ajax修改具体信息后需要将对应的新的内容采用jQuery显示到标题中。
![这里写图片描述](https://img-blog.csdn.net/20180506142810540?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
![这里写图片描述](https://img-blog.csdn.net/20180506142815483?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.8 我发布的商品实现
  显示个人发表的商品信息,支持点击之后对数据进行修改,例如修改商品名字,修改商品的单价和数量等,修改之后将新的数据提交到数据库,数据库执行相应的修改操作,也可以直接下架某件商品,点击删除按钮之后,直接在后台数据库删除这一件商品,并且刷新当前页面上显示的个人发布的商品。给用户带来更好的体验效果。
![这里写图片描述](https://img-blog.csdn.net/20180506142820866?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
  点击修改
![这里写图片描述](https://img-blog.csdn.net/2018050614282592?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.9我发布的求购信息实现
  显示个人发表的求购商品信息,支持点击之后对数据进行修改,例如修改商品名字,修改商品的单价和数量等,修改之后将新的数据提交到数据库,数据库执行相应的修改操作,也可以直接下架某件商品,点击删除按钮之后,直接在后台数据库删除这一件商品,并且刷新当前页面上显示的个人求购的商品。
![这里写图片描述](https://img-blog.csdn.net/2018050614284365?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
![这里写图片描述](https://img-blog.csdn.net/20180506142847753?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.10购物车实现
显示用户加入购物车的商品计算总的价格提供全选和取消全选的按钮从后台获取商品的单价判断当前剩余的数量当用户点击数量添加或者减少之后对于总价要实时刷新采用jQuery对数据进行修改当用户取消选中其中的某一个商品要相应的减少价格并且刷新提供选择收货地址对收获地址进行管理选好收货地址之后结算购物车弹出支付页面让用户选择付款方式并且提交。
![这里写图片描述](https://img-blog.csdn.net/20180506142855913?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
  点击添加数量和减少
![这里写图片描述](https://img-blog.csdn.net/20180506142901134?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
### 3.2.11登录注册实现
  用户通过手机号码和密码进行登录登录之后显示首页。当用户没有账号的时候提示用户进行注册从登录界面切换到注册界面注册需要通过手机号码获取验证码后台通过页面传递的手机号码随机生成4位数的验证码并且缓存之后通过发送139邮箱的方式发送到指定的手机手机收取到验证码之后输入验证码提交判断是否正确正确则注册成功失败则注册失败。用户注册完之后直接跳转到首页。
  登录界面需要4个页面第一个是登录界面显示手机号码和密码的组件并且需要底部添加注册按钮以及忘记密码。第二个是注册界面注册界面首先提供用户输入手机号码以及验证码获取验证码的按钮获取之后跳转到下一步也就是输入密码界面。输入密码界面需要两次确认密码输入确认之后就是跳转到首页。最后一个界面是忘记密码同样通过输入手机号码获取验证码即可。之后将这四个界面通过CSS3的transform属性将四个界面组合成一个立方体当用户点击切换到各自界面的按钮时就采用transform的rotateY将切换的效果实现成立体翻转的效果。
![这里写图片描述](https://img-blog.csdn.net/20180506142908919?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
![这里写图片描述](https://img-blog.csdn.net/20180506142916295?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
![这里写图片描述](https://img-blog.csdn.net/20180506142923392?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
##### 后台实现的过程
  1. 总述本次后台使用的编程语言为Java使用的框架是Spring+SpringMVC+Mybatis前端网页渲染的语言不使用JSP而我选择使用的是thymeleaf框架  
  原因如下:
  a. thymeleaf可以直接渲染出HTML5和CSS3的效果。
  b.前端写的网页合并到后台处理的时候不需要转换成jsp页面方便快捷。
  c. thymeleaf可以很方便的解析后台传输到前端的值。
  d. thymeleaf我个人认为是个比较好的模板性能也比一般的比如freemaker的要高而且把将美工和程序员能够结合起来美工能够在浏览器中查看静态效果程序员可以在应用服务器查看带数据的效果。
  e. thymeleaf是一个支持html原型的自然引擎它在html标签增加额外的属性来达到模板+数据的展示方式由于浏览器解释html时忽略未定义的标签属性因此thymeleaf的模板可以静态运行。
  2. 后台结构总图
![这里写图片描述](https://img-blog.csdn.net/20180506142931448?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dzazExMDM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
  java:存放Java代码
  bean用于序列化到前端然后解析的实体类。
  controllerspring和springMVC的控制器用于处理请求的链接
  dao和数据库进行交互
  pojo普通的Java实体类
  service定义接口用于连接controller和dao
  token生成唯一标识用于防止用户重复提交。
  tool工具类例如处理文件文本敏感词过滤图片色情鉴别等等。
  resources存放资源文件例如Mybatis生成的xml文件springspringMVCmybatis配置文件等等
  css网站样式
  img网站部分图片
  jsJavaScript文件
  templates网站html文件
  3.后台实现了一个只能单用户登录的功能如果有两个用户使用同一个账号分别登录那么后一个登录的用户会踢掉第一个用户登录的账号也就是说先登录的用户会被强制退出。实现细节主要使用的是websockt和spring websockt。首先前端判断用户有没有登录如果有登录的话就将用户的手机号码当前时间发送到后台后台接收到信息解析信息将信息存储到Map中前台循环提交这些信息并判断Map中手机号码对应的时间是否相等如果不相等证明有其他用户登录了该账号后台发送下线信息到前端前端收到信息后触发退出当前账号事件。
  4.常规数据库增删查改考虑到密码是重要的信息所以当操作密码的时候都是先使用MD5对其进行加密后处理。例如修改密码注册用户等等。各个数据表之间的关联都是通过逻辑处理进行数据联系的。
  5.防止用户重复提交在每个界面都会由后台生成一个token然后前端页面存储该token如果用户进行提交按钮等事件时就会连这个token一起发送到后台进行处理后台若获取不到token或者token不相匹配那么就判定该用户重复提交这个事件后台直接返回原本页面。
  6.敏感词过滤:管理员将敏感词编入到数据库中,用户发布商品时附带的商品名称,商品备注等等,都会被过滤,将其中的敏感词替代成**。然后返回对应的数值。用户在填写商品名称详情的时候还会ajax自动和后台进行验证判断并实时提醒用户有没有输入敏感词。
  7.图片色情鉴别:使用的是百度的百度开发者平台中的色情图片识别功能。通过提交一张图片到百度的这个功能中,该项目功能就会返回一个数字,姑且称该值为色情值,如果该色情值超过一个给定的数值,那么就可以判定该图片为色情图片,如果用户上传的是色情图片,那么就无法插入,修改,后台会直接返回一个错误值给前端,提示使用了非法的图片。
## 4.问题分析
  4.1 在实现只能单用户登录的时候由于是第一次编写以前没有经验刚开始的想法是设计2个Mapm1,m2用于存储前一次登录m1和再次登录m2的手机号码和对应的时间。首先当用户第一次登录的时候m1和m2必定都为空m2先通过手机号码存储当前的时间然后前端通过websockt发送定时信息过来请求判断在后台处理前端发送过来的信息的时先判断m1是否存在m2中该的该手机如果不存在则证明为第一次登录那么m1存储m2中对应手机的对应时间。如果存在那么将m1中时间和m2中的时间进行比较若比较结果一致是单用户登录若不一致该手机为前一次登录强迫下线。当用户再次登录的时候m2中存储手机对应的当前的时间那么前一次登录的时候m1中存储的手机对应的时间必然小于再次登录中m2中存储的时间此时前一次登录强迫下线m1中的时间刷新为m2中对应的时间。但是这个思路出现了一个严重的问题在测试的时候首次登录后第二次登录确实会踢掉第一次登录但是第三次登录的时候所有的用户都平安无事不会互相排挤此时出现的问题应该是线程不安全导致的结果。m1m2这2个map都可能会出现这种情况。
  解决方法设计一个Mapm用于存储手机号码和其对应的时间。首先用户登录后将当前的时间手机号码通过websockt发送到后台后台进行判断处理如果发送过来的时间和m中存储的时间进行比较若一致证明没有其他用户登录若不一致证明其他用户登录过那么当前这次用户就会被迫下线
  4.2 设计的工具类中因为存在很多个类和包如果没有使用工厂模式进行设计的时候那么在启动项目的时候多用户登录时就会导致线程不安全或者线程堵塞并且将类中的所有方法声明为static时会占用了许多的内存。
  解决方法:使用静态工厂模式-静态内部类。将所有的工具类集中到一个总类中然后为给总类设计一个静态内部类其他将要调用的方法设计为public非静态方法如果调用的时候用工厂调用就可以了。
  4.3 当使用多个参数通过mybatis获取数据库数据时出现了参数未定义的情况这是因为多个参数的时候要将这些参数封装为一个Map。
  解决方法将多个参数封装为一个Map然后将该Map作为参数传递给mybatis的处理方法。
  4.4 发送手机验证码出现的错误:一开始像使用邮箱验证用户的,但是在通过邮箱给用法发送信息的时候,这个时候就有多个邮箱作发送者供选择-QQ,163,127,sina等等。原先使用的是163邮箱进行发送信息但是由于发送的内容过于少被系统认定为垃圾邮件不给发送或者接收。其他邮箱也类似。后来感觉使用邮箱发送验证码已经被淘汰了现在流行手机验证了所以改为发送手机验证码。但是手机发送验证码的话不想使用网上别人提供的服务打算自己写一个关键是怎么将验证码发送到手机上。
  解决方法将验证码发送到139邮箱上就可以了一般的手机用户都会默认绑定139邮箱的如果139邮箱收到信息那么就会通过发送短信的形式提示到对应的手机上这样就可以实现手机验证码的发送了。
  4.5 前端使用Ajax局部刷新时有可能会导致新显示的内容无法绑定点击事件最终导致在点击相应的按钮例如加入购物车按钮页面没有反应。
  解决方法在Ajax的for循环显示结果内容后为按钮等绑定相应的事件。这样就可以解决该问题。
## 6.用户使用说明
### 6.1 首页
  首先访问本站网址进入首页可以浏览首页的商品信息首页轮播图可以左右切换查看商品信息如果没有主动切换那么每3.5s之后会自动往右切换一张,往下滑动显示的是其他的一些热门商品缩略信息,点击商品名字之后可以查看该商品的详细信息和留言。再下方是一些其他信息,可以忽略。
### 6.2 普通游客的限制
  普通游客可以随意访问首页,可以通过导航栏访问商城,求购商城等。但是如要要发布求购、发布商品则需要先登录。
### 6.3 没有账号?
  如果用户还没有账号,那么点击右上角的注册按钮,之后会跳转到注册界面,用户需要输入手机号,再通过它获取验证码,注意输入的手机号必须要格式正确并且有效,获取验证码之后,便点击下一步,此时到达的界面是确认密码界面,重复输入密码,两次输入的密码必须一致。确认完毕之后就会进入首页。如果用户忘记了自己注册过的账号密码,那么点击登录界面的忘记密码选项,输入注册时使用的手机号码,通过此手机号码获取验证码,输入验证码之后,点击下一步开始重置密码,重置完毕之后就会重新进入到首页。
### 6.4 商城
  用户想要查看其他用户发布的商品需要通过点击导航栏处的商城进入到商城页面在此可以浏览所有的商品每页显示12个底部的分页按钮可以点击之后切换到用户想找的页面。而用户如果需要过滤信息找到自己想要的类别左侧悬浮的悬浮框鼠标移动到指定的类别中查看详细的类别点击自己想要找的商品类别网站便会过滤出指定的商品呈现给用户。
  商城显示的商品信息只是一部分,通过点击商品名可以跳转到商品详细信息页面,用户可以在此查看商品的所有信息,可以将它添加到购物车,或者查看下方其他用户的留言,也可以针对自己的意见,对商品做出评价。
### 6.5 模糊查询
  如果想要模糊查询商品,最顶部有一个输入框,输入商品的信息,点击搜索按钮,系统会自动过滤合适的商品呈现给用户。
### 6.6 求购商城
  如果用户有商品想要出售,可以进入求购商城,浏览其他用户的求购信息,通过点击顶部的导航栏 求购商城 进入查看所有的求购信息,同样有分页和侧边栏的功能。
### 6.7 发布商品信息
  如果已经登录过,那么右上角显示的是用户名,将鼠标移动到上面,会弹出一个下拉框,点击发布商品信息,按照提示输入各自的信息,之后确认信息无误之后点击发布按钮即可。
### 6.8 发布求购信息
  发布求购信息的过程跟发布商品信息一样,主要是点击下拉框的发布求购信息,在发表即可。
### 6.9 查看我发布的商品
  鼠标移动到左上角的用户名,点击显示出来的下拉框的我发布的商品,就可以进入到自己发布过的商品,可以点击每一个商品下方的修改图标可以对商品进行修改,点击删除图片可以下架这个商品。
### 6.10 查看我的求购商品
  跟查看我发布的商品基本一致,只是点击的是我的求购商品。
### 6.11 购物车
  用户浏览到合适的商品,可以点击添加至购物车的按钮,之后可以将鼠标移动至右上角用户名处,点击显示出来的下拉列表,点击我的购物车,进入到我的购物车查看自己有意向购买的商品。此时可以选择收货地址,或者修改购物车中的一些信息,例如修改数量,或者删除某一件商品。也可以同时选中所有的商品,或者是取消选中所有的商品,之后便可以结算。
# 三、设计总结或结论
  该C2C校园二手交易平台界面简洁明了能给用户一种美的视觉上的享受。并且操作简单易懂可以大胆但是毫不夸张的说本系统适用于99%的青少年愉快的使用对于不是很了解智能科技的人来说只要看看就基本可以掌握然后熟练的使用该系统了。并且本系统的动画效果做得非常的完美前端工作人员争取使用最少的代码实现最炫酷的动态效果例如3D页面转换动态加入购物车效果搜索的时候动态自动提示商品名字功能等等无一不是本系统的亮点。由于前端本着使用最少的代码实现最炫酷的动态效果所以其性能可以来说是杠杠的。并且前端所构建的网站为响应式网站当页面的大小发送变化的时候页面会随着变化但是我们的变化不会出现任何的纰漏。在兼容性方面本网站可以完美的兼容ie8+Firefox8+chrome6+等等,虽然目前比较少用户使用这些版本以下的浏览器,但是要做的话,我们就要力争做到完美,前端人员通过查询资料,目前可以最完美的兼容上诉浏览器的以下版本。
  前端页面缺点对于那些不兼容HTML5和CSS3的浏览器使用的是JavaScript来动态实现这些3D效果的所以可能展示的效果不如支持HTML5和CSS3的本身效果好。对应浏览器版本真的是过低的用户无法完美的显示本系统的动态网页。JavaScript实现的那些动画效果可能存在某些目前未知的bug导致性能上的加载可能会出现瑕疵。
  后台实现的优点:后台为了提高性能,使用了一些目前属于比较好的设计模式,例如工厂方法模式,代理模式,备忘录模式,单例模式等等。数据库表的设计思想主要是参考阿里巴巴的开发手册进行设计的,在性能上,建立了适当的索引,增强了查询速度,并且自己设计了许多的算法对后台数据的处理进行优化等等。本来想使用集群和分布式来实现后台,但是考虑到现在如果使用集群,分布式来实现,那么开发周期得提升不止一个档次的时间。
  网站加载图片的时候通常耗费的主要流量和影响速度的原因之一就是这个所以后台设计了一个比较有意思的算法对图片进行适当的压缩在前端显示的时候主要显示的压缩后的图片当鼠标移到图片上的时候那么通过ajax显示未被压缩过的图片的放大版。这样既可以节省带宽又可以提高用户的体验。
  为了防止XSS攻击可以使用消毒和HttpOnly防止该现象对于注入攻击使用的方法为错误回显盲注参数绑定对于CSRF攻击可以使用表单token验证码和Rererfer check。
  对于密码,手机号码,真实信息等等敏感信息,通常都需要对其进行信息加密,我使用的是单向散列表加密和对称加密,还有就是使用公钥密钥配对核实认证。但是如果使用到密钥的话,那么一般会要求用户下载安全证书到本机。
  信息过滤和反垃圾是目前一个比较主流的问题。我使用的主要是文本匹配来过滤敏感词的。当用户发布的信息很长的时候使用的过滤算法是双数组Trie算法进行敏感词过滤该算法利用两个稀疏数组存储树结构base数组存储Trie树的节点check数组进行状态检查。该算法需要根据业务场景和经验确定数组大小避免数组过大或者冲突过多。
  简而言之,前端效果简洁炫酷,操作简单易懂。
  没有实现的功能秒杀系统前端反向代理CDN加速。
## 参考文献
[1] 李智慧. 大型网站技术架构 核心原理与案例分析[M].北京工业机械出版社2013.
[2] 马丁著 韩磊译.代码整洁之道[Clean Code A Handbook of Agile Software Craftsmanship] [M]. 北京人们邮电出版社2010.
[3] Craig Walls 著 张卫滨译. Spring In Action[M]. 北京人民邮电出版社2016
[4] Thomas H.Cormen , Charles E.Leiserson, Ronald L.Rivest, Clifford Stein著徐云王刚等译. 算法导论原书第3版[M]. 北京机械工业出版社2015
[5] Joshua Bloch 著 杨春花 俞黎敏 译. Effective Java(Second Edition) [M]. 北京机械工业出版社2016
[6] 本·弗莱恩Ben Frain著奇舞团 译. 响应式Web设计 HTML5和CSS3实战第2版[M]. 人民邮电出版社2017
[7] 达科特Jon Duckett 著 刘涛,陈学敏 译. JavaScript&jQuery交互式Web前端开发[M]. 清华大学出版社2015
[8] NicholasC.Zakas著 李松峰曹力 译. JavaScript高级程序设计(第3版)[M]. 人民邮电出版社2016

@ -0,0 +1,109 @@
package com.wsk.bean;
import java.io.Serializable;
import java.util.Date;
/**
* Created by wsk1103 on 2017/5/22.
*/
public class GoodsCarBean implements Serializable{
private int id;
private int uid;
private int sid;
private String name;
private Date modified;
private int quantity;
private int level;
private String remark;
private String sort;
private String image;
private double price;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getModified() {
return modified;
}
public void setModified(Date modified) {
this.modified = modified;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getSort() {
return sort;
}
public void setSort(String sort) {
this.sort = sort;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}

@ -0,0 +1,64 @@
package com.wsk.bean;
import java.io.Serializable;
import java.util.Date;
/**
* Created by wsk1103 on 2017/5/21.
*/
public class ShopContextBean implements Serializable {
private int id;
private Date modified;
private int sid;
private String context;
private int uid;
private String username;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getModified() {
return modified;
}
public void setModified(Date modified) {
this.modified = modified;
}
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getContext() {
return context;
}
public void setContext(String context) {
this.context = context;
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}

@ -0,0 +1,131 @@
package com.wsk.controller;
import com.wsk.pojo.UserInformation;
import com.wsk.response.BaseResponse;
import com.wsk.service.UserInformationService;
import com.wsk.tool.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
/**
* Created by wsk1103 on 2017/4/30.
*/
@Controller
public class SendEmailController {
@Resource
private UserInformationService userInformationService;
private static final Logger log = LoggerFactory.getLogger(SendEmailController.class);
//send the Email to the phone
@RequestMapping(value = "sendCode.do", method = {RequestMethod.POST, RequestMethod.GET})
@ResponseBody
public BaseResponse sendEmail(HttpServletRequest req, HttpServletResponse res,
@RequestParam String phone, @RequestParam String action,
@RequestParam String token) {
res.setContentType("text/html;charset=UTF-8");
//token防止重复提交
String sendCodeToken = (String) req.getSession().getAttribute("token");
if (StringUtils.getInstance().isNullOrEmpty(sendCodeToken) || !sendCodeToken.equals(token)) {
return BaseResponse.fail();
}
//判断手机号码是否为正确
if (!StringUtils.getInstance().isPhone(phone)) {
return BaseResponse.fail();
}
//如果是忘记密码提交的发送短信
if ("forget".equals(action)) {
if (!isUserPhoneExists(phone)) {
//失败
return BaseResponse.fail();
}
} else if ("register".equals(action)) {
//失败
if (isUserPhoneExists(phone)) {
return BaseResponse.fail();
}
}
//get the random num to phone which should check the phone to judge the phone is belong user
getRandomForCodePhone(req);
String ra = (String) req.getSession().getAttribute("codePhone");
String text1 = "【WSK的验证码】您的验证码是";
String text2 = ",请保护好自己的验证码。";
String text = text1 + ra + text2;
Properties prop = new Properties();
prop.setProperty("mail.host", "smtp.139.com");
prop.setProperty("mail.transport.protocol", "smtp");
prop.setProperty("mail.smtp.auth", "true");
prop.setProperty("mail.smtp.port", "25");
try {
String realPhone = phone;
// phone += "@139.com";
// message.setRecipient(Message.RecipientType.TO, new InternetAddress(phone));
// message.setSubject("来自WSK的验证码");
// message.setContent(text, "text/html;charset=UTF-8");
//这里先不发生信息,以后要开启的
// ts.sendMessage(message, message.getAllRecipients());
// ts.close();
req.getSession().setAttribute("phone", realPhone);
return BaseResponse.success();
} catch (Exception me) {
me.printStackTrace();
return BaseResponse.fail();
}
}
// get the random phone`s code
private void getRandomForCodePhone(HttpServletRequest req) {
Random random = new Random();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 4; i++) {
sb.append(random.nextInt(10));
}
log.info("短信验证码={}", sb);
System.out.println(sb.toString());
req.getSession().setAttribute("codePhone", sb.toString());
}
// //检验验证码
// private boolean checkPhoto(String photo, HttpServletRequest request) {
// photo = photo.toLowerCase();
// String true_photo = (String) request.getSession().getAttribute("rand");
// return true_photo.equals(photo);
// }
//To determine whether the user's mobile phone number exists
private boolean isUserPhoneExists(String phone) {
boolean result = false;
try {
int id = userInformationService.selectIdByPhone(phone);
if (id == 0) {
return result;
}
UserInformation userInformation = userInformationService.selectByPrimaryKey(id);
if (StringUtils.getInstance().isNullOrEmpty(userInformation)) {
return false;
}
String userPhone = userInformation.getPhone();
result = !userPhone.equals("");
} catch (Exception e) {
e.printStackTrace();
return result;
}
return result;
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,19 @@
package com.wsk.dao;
import com.wsk.pojo.AdminInformation;
public interface AdminInformationMapper {
int deleteByPrimaryKey(Integer id);
int insert(AdminInformation record);
int insertSelective(AdminInformation record);
AdminInformation selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(AdminInformation record);
int updateByPrimaryKey(AdminInformation record);
AdminInformation selectByNo(int ano);
}

@ -0,0 +1,21 @@
package com.wsk.dao;
import com.wsk.pojo.GoodsCar;
import java.util.List;
public interface GoodsCarMapper {
int deleteByPrimaryKey(Integer id);
int insert(GoodsCar record);
int insertSelective(GoodsCar record);
GoodsCar selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(GoodsCar record);
int updateByPrimaryKey(GoodsCar record);
List<GoodsCar> selectByUid(int uid);
}

@ -0,0 +1,23 @@
package com.wsk.dao;
import com.wsk.pojo.ShopCar;
public interface ShopCarMapper {
int deleteByPrimaryKey(Integer id);
int insert(ShopCar record);
int insertSelective(ShopCar record);
ShopCar selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(ShopCar record);
int updateByPrimaryKey(ShopCar record);
int getCounts(int uid);
// List<ShopCar> selectByUid(int uid, int start);
ShopCar selectByUid(int uid);
}

@ -0,0 +1,27 @@
package com.wsk.dao;
import com.wsk.pojo.ShopContext;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface ShopContextMapper {
int deleteByPrimaryKey(Integer id);
int insert(ShopContext record);
int insertSelective(ShopContext record);
ShopContext selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(ShopContext record);
int updateByPrimaryKey(ShopContext record);
int getCounts(int sid);
List<ShopContext> findById(int sid, int start);
@Select("select * from shopcontext where sid=#{id,jdbcType=INTEGER} and display=1")
List<ShopContext> selectBySid(int id);
}

@ -0,0 +1,46 @@
package com.wsk.pojo;
import java.io.Serializable;
import java.util.Date;
public class AdminInformation implements Serializable{
private Integer id;
private String ano;
private String password;
private Date modified;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getAno() {
return ano;
}
public void setAno(String ano) {
this.ano = ano == null ? null : ano.trim();
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password == null ? null : password.trim();
}
public Date getModified() {
return (Date) modified.clone();
}
public void setModified(Date modified) {
this.modified = (Date) modified.clone();
}
}

@ -0,0 +1,66 @@
package com.wsk.pojo;
import java.io.Serializable;
import java.util.Date;
public class GoodsCar implements Serializable {
private Integer id;
private Date modified;
private Integer sid;
private Integer uid;
private Integer quantity;
private Integer display;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Date getModified() {
return modified == null ? null : (Date) modified.clone();
}
public void setModified(Date modified) {
this.modified = modified == null ? null : (Date) modified.clone();
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
public Integer getDisplay() {
return display;
}
public void setDisplay(Integer display) {
this.display = display;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
}

@ -0,0 +1,46 @@
package com.wsk.pojo;
import java.io.Serializable;
import java.util.Date;
public class ShopCar implements Serializable {
private Integer id;
private Date modified;
private Integer display;
private Integer uid;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Date getModified() {
return modified == null ? null : (Date) modified.clone();
}
public void setModified(Date modified) {
this.modified = modified == null ? null : (Date) modified.clone();
}
public Integer getDisplay() {
return display;
}
public void setDisplay(Integer display) {
this.display = display;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
}

@ -0,0 +1,66 @@
package com.wsk.pojo;
import java.io.Serializable;
import java.util.Date;
public class ShopContext implements Serializable {
private Integer id;
private Date modified;
private Integer sid;
private String context;
private Integer display;
private int uid;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Date getModified() {
return modified == null ? null : (Date) modified.clone();
}
public void setModified(Date modified) {
this.modified = modified == null ? null : (Date) modified.clone();
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getContext() {
return context;
}
public void setContext(String context) {
this.context = context == null ? null : context.trim();
}
public Integer getDisplay() {
return display;
}
public void setDisplay(Integer display) {
this.display = display;
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
}

@ -0,0 +1,62 @@
package com.wsk.service.Impl;
import com.wsk.dao.ShopCarMapper;
import com.wsk.pojo.ShopCar;
import com.wsk.service.ShopCarService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* Created by wsk1103 on 2017/5/13.
*/
@Service
public class ShopCarServiceImpl implements ShopCarService {
@Resource
private ShopCarMapper shopCarMapper;
@Override
public int deleteByPrimaryKey(Integer id) {
return 0;
}
@Override
public int insert(ShopCar record) {
return shopCarMapper.insert(record);
}
@Override
public int insertSelective(ShopCar record) {
return shopCarMapper.insertSelective(record);
}
@Override
public ShopCar selectByPrimaryKey(Integer id) {
return shopCarMapper.selectByPrimaryKey(id);
}
@Override
public int updateByPrimaryKeySelective(ShopCar record) {
return shopCarMapper.updateByPrimaryKeySelective(record);
}
@Override
public int updateByPrimaryKey(ShopCar record) {
return shopCarMapper.updateByPrimaryKey(record);
}
@Override
public int getCounts(int uid) {
return shopCarMapper.getCounts(uid);
}
// @Override
// public List<ShopCar> selectByUid(int uid, int start) {
// return shopCarMapper.selectByUid(uid, start);
// }
@Override
public ShopCar selectByUid(int uid) {
return shopCarMapper.selectByUid(uid);
}
}

@ -0,0 +1,26 @@
package com.wsk.service;
import com.wsk.pojo.ShopCar;
/**
* Created by wsk1103 on 2017/5/13.
*/
public interface ShopCarService {
int deleteByPrimaryKey(Integer id);
int insert(ShopCar record);
int insertSelective(ShopCar record);
ShopCar selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(ShopCar record);
int updateByPrimaryKey(ShopCar record);
int getCounts(int uid);
// List<ShopCar> selectByUid(int uid, int start);
ShopCar selectByUid(int uid);
}

@ -0,0 +1,28 @@
package com.wsk.service;
import com.wsk.pojo.ShopContext;
import java.util.List;
/**
* Created by wsk1103 on 2017/5/13.
*/
public interface ShopContextService {
int deleteByPrimaryKey(Integer id);
int insert(ShopContext record);
int insertSelective(ShopContext record);
ShopContext selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(ShopContext record);
int updateByPrimaryKey(ShopContext record);
int getCounts(int sid);
List<ShopContext> findById(int sid, int start);
List<ShopContext> selectById(int id);
}

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.wsk.dao.GoodsCarMapper">
<resultMap id="BaseResultMap" type="com.wsk.pojo.GoodsCar">
<id column="id" property="id" jdbcType="INTEGER"/>
<result column="modified" property="modified" jdbcType="TIMESTAMP"/>
<result column="sid" property="sid" jdbcType="INTEGER"/>
<result column="uid" property="uid" jdbcType="INTEGER"/>
<result column="quantity" property="quantity" jdbcType="INTEGER"/>
<result column="display" property="display" jdbcType="INTEGER"/>
</resultMap>
<sql id="table">
goodscar
</sql>
<sql id="all">
id, modified, sid, uid, quantity, display
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer">
select
<include refid="all"/>
from
<include refid="table"/>
where id = #{id,jdbcType=INTEGER}
</select>
<select id="selectByUid" resultMap="BaseResultMap" parameterType="java.lang.Integer">
select
<include refid="all"/>
from
<include refid="table"/>
where uid = #{uid,jdbcType=INTEGER} and display=1 order by id DESC
limit 12
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
delete from
<include refid="table"/>
where id = #{id,jdbcType=INTEGER}
</delete>
<insert id="insert" parameterType="com.wsk.pojo.GoodsCar">
insert into
<include refid="table"/>
(id, modified, sid,
uid, quantity, display
)
values (#{id,jdbcType=INTEGER}, #{modified,jdbcType=TIMESTAMP}, #{sid,jdbcType=INTEGER},
#{uid,jdbcType=INTEGER}, #{quantity,jdbcType=INTEGER}, #{display,jdbcType=INTEGER}
)
</insert>
<insert id="insertSelective" parameterType="com.wsk.pojo.GoodsCar">
insert into
<include refid="table"/>
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="modified != null">
modified,
</if>
<if test="sid != null">
sid,
</if>
<if test="uid != null">
uid,
</if>
<if test="quantity != null">
quantity,
</if>
<if test="display != null">
display,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=INTEGER},
</if>
<if test="modified != null">
#{modified,jdbcType=TIMESTAMP},
</if>
<if test="sid != null">
#{sid,jdbcType=INTEGER},
</if>
<if test="uid != null">
#{uid,jdbcType=INTEGER},
</if>
<if test="quantity != null">
#{quantity,jdbcType=INTEGER},
</if>
<if test="display != null">
#{display,jdbcType=INTEGER},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.wsk.pojo.GoodsCar">
update
<include refid="table"/>
<set>
<if test="modified != null">
modified = #{modified,jdbcType=TIMESTAMP},
</if>
<if test="sid != null">
sid = #{sid,jdbcType=INTEGER},
</if>
<if test="uid != null">
uid = #{uid,jdbcType=INTEGER},
</if>
<if test="quantity != null">
quantity = #{quantity,jdbcType=INTEGER},
</if>
<if test="display != null">
display = #{display,jdbcType=INTEGER},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
<update id="updateByPrimaryKey" parameterType="com.wsk.pojo.GoodsCar">
update
<include refid="table"/>
set modified = #{modified,jdbcType=TIMESTAMP},
sid = #{sid,jdbcType=INTEGER},
uid = #{uid,jdbcType=INTEGER},
quantity = #{quantity,jdbcType=INTEGER},
display = #{display,jdbcType=INTEGER}
where id = #{id,jdbcType=INTEGER}
</update>
</mapper>
Loading…
Cancel
Save