前台需求分析
一:用户模块
- 注册
- 前台JS校验
- 使用AJAX完成对用户名(邮箱)的异步校验
- 后台Struts2校验
- 验证码
- 发送激活邮件
- 将用户信息存入到数据库
- 激活
- 点击激活邮件中的链接完成激活
- 根据激活码,查询数据库中是否有该用户;
- 如果有则激活,否则不激活;
- 登录
- 输入用户名、密码、验证码(前台,后台数据校验)
- 用户名、密码都正确同时用户状态必须是激活状态
- 退出
- 销毁session。
二:一级分类模块
- 查询一级分类
- 将一级分类存入session中(每个页面都一样的数据)
- 查询某个一级分类
- 查询所有一级分类,同时查询到该一级分类下的所有二级分类
- 查询该分类下所有的商品,带分页显示
三:商品模块
- 查询热门商品
- 查询热门商品:限制查10个
- 查询最新商品
- 查询最新商品:限制查10个
- 查询某个分类商品
- 根据分类的ID进行查询商品
- 查询某个二级分类商品
- 根据二级分类的ID进行查询商品
- 查询某个商品信息
- 根据商品ID进行查询商品
四:购物模块
- 添加到购物车
- 将商品添加到购物车
- 从购物车中移除商品
- 将商品从购物车中移除
- 清空购物车
- 将所有的商品信息从购物车中移除
五:订单模块
- 生成订单
- 将购物车中的信息存入到数据库(生成订单)
- 清空购物车
- 为订单付款
- 在线支付功能
- 修改订单状态
- 修改订单信息(收货地址,联系方式,收货人等)
- 查询我的订单
- 根据用户ID查询订单
- 查询某个订单详情
- 根据订单ID查询订单
后台需求分析
一:用户模块
- 添加用户 · 后台只添加管理员用户
- 删除用户
- 查询用户
- 修改用户
二:一级分类
- 添加一级分类
- 删除一级分类
- 级联删除一级分类下的所有二级分类
- 修改一级分类
- 级联修改一级分类下的所有二级分类
- 查询一级分类
- 分页查询
三:二级分类
- 添加二级分类
- 选择所属的一级分类进行添加
- 删除二级分类
- 删除二级分类下的所有商品
- 修改二级分类
- 查询二级分类
- 分页查询
五:商品模块
- 添加商品
- 选择商品所属二级分类进行添加
- 上传商品图片
- 修改商品
- 修改商品二级分类
- 修改商品图片
- 删除商品
- 查询商品
- 商品查询(带分页)
六:订单模块
- 查询订单
- 查询所有订单(带分页)
- 异步加载所有订单
页面设计
完成前台静态页面的设计。
数据库设计
原则:至少一个模块一张表。
用户表:
- 用户ID
- 用户名
- 密码
- 真实姓名
- 邮箱
- 细致
- 电话
- 用户状态:0--激活;1--未激活
- 激活码
sql语句:
CREATE TABLE `user` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`phone` varchar(255) DEFAULT NULL,
`addrress` varchar(255) DEFAULT NULL,
`state` int(11) DEFAULT NULL,
`code` varchar(64) DEFAULT NULL,
PRIMARY KEY (`uid`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
一级分类表:
- 一级分类ID
- 一级分类名称
CREATE TABLE `category` (
`cid` int(11) NOT NULL AUTO_INCREMENT,
`cname` varchar(255) DEFAULT NULL,
PRIMARY KEY (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;
二级分类表:
- 二级分类ID
- 二级分类名称
- 一级分类ID(外键指向一级分类主键ID)
CREATE TABLE `categorysecond` (
`csid` int(11) NOT NULL AUTO_INCREMENT,
`csname` varchar(255) DEFAULT NULL,
`cid` int(11) DEFAULT NULL,
PRIMARY KEY (`csid`),
KEY `FK936FCAF21DB1FD15` (`cid`),
CONSTRAINT `FK936FCAF21DB1FD15` FOREIGN KEY (`cid`) REFERENCES `category` (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=71 DEFAULT CHARSET=utf8;
商品表:
- 商品ID
- 商品名称
- 商品商城价格
- 商品描述
- 商品市场价格
- 商品描述
- 商品图片(路径)
- 二级分类ID(外键指向二级分类主键ID )
CREATE TABLE `product` (
`pid` int(11) NOT NULL AUTO_INCREMENT,
`pname` varchar(255) DEFAULT NULL,
`market_price` double DEFAULT NULL,
`shop_price` double DEFAULT NULL,
`image` varchar(255) DEFAULT NULL,
`pdesc` varchar(255) DEFAULT NULL,
`is_hot` int(11) DEFAULT NULL,
`pdate` datetime DEFAULT NULL,
`csid` int(11) DEFAULT NULL,
PRIMARY KEY (`pid`),
KEY `FKED8DCCEFB9B74E02` (`csid`),
CONSTRAINT `FKED8DCCEFB9B74E02` FOREIGN KEY (`csid`) REFERENCES `categorysecond` (`csid`)
) ENGINE=InnoDB AUTO_INCREMENT=86 DEFAULT CHARSET=utf8;
订单表:
- 订单ID
- 订单时间
- 订单金额
- 订单状态
- 订单地址
- 订单电话
- 订单收货人
- 用户ID(外键指向用户表的主键ID)
CREATE TABLE `orders` (
`oid` int(11) NOT NULL AUTO_INCREMENT,
`total` double DEFAULT NULL,
`ordertime` datetime DEFAULT NULL,
`state` int(11) DEFAULT NULL,
`name` varchar(20) DEFAULT NULL,
`phone` varchar(20) DEFAULT NULL,
`addr` varchar(50) DEFAULT NULL,
`uid` int(11) DEFAULT NULL,
PRIMARY KEY (`oid`),
KEY `FKC3DF62E5AA3D9C7` (`uid`),
CONSTRAINT `FKC3DF62E5AA3D9C7` FOREIGN KEY (`uid`) REFERENCES `user` (`uid`)
) ENGINE=InnoDB AUTO_INCREMENT=9004 DEFAULT CHARSET=utf8;
订单项表:(需要参与业务逻辑中)
- 主键ID
- 商品ID(指向)
- 订单ID()
- 数量
- 小计
CREATE TABLE `orderitem` (
`itemid` int(11) NOT NULL AUTO_INCREMENT,
`count` int(11) DEFAULT NULL,
`subtotal` double DEFAULT NULL,
`pid` int(11) DEFAULT NULL,
`oid` int(11) DEFAULT NULL,
PRIMARY KEY (`itemid`),
KEY `FKE8B2AB6166C01961` (`oid`),
KEY `FKE8B2AB6171DB7AE4` (`pid`),
KEY `FKE8B2AB6140ACF87A` (`oid`),
CONSTRAINT `FKE8B2AB6140ACF87A` FOREIGN KEY (`oid`) REFERENCES `orders` (`oid`),
CONSTRAINT `FKE8B2AB6171DB7AE4` FOREIGN KEY (`pid`) REFERENCES `product` (`pid`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;
后台用户表:
- 用户名
- 密码
表关系分析图
其中 : 订单项表是商品表和订单表之间多对多关系产生的中间表。
编码实现
一:SSH整合
- 参考之前得SSH项目实战 · 项目环境搭建——http://www.cnblogs.com/panweiwei/p/6212609.html。
2. 引入前端页面 ---- 页面都放到WebRoot/WEB-INF下。
- 引入静态页面
- 将HTML静态页面都复制到WebRoot/WEB-INF/jsp目录下.
- 将JS、CSS文件分别复制到WebRoot/js、WebRoot/css目录下.
3. 访问首页
- 创建包的格式:
* cn.itcast.shop.index
* action
* cn.itcast.shop.user
* action
* service
* dao
* vo
- 编写Action
- IndexAction
- 改造jsp和文件路径
- 将html静态页面改为jsp页面文件
- 将jsp中的css、js、图片路径改为绝对路径(${pageContext.request.contextPath} )
- 配置Action
- 配置applicationContext.xml
<!-- 首页访问的Action 注意:千万不要忘记scope-->
<bean id="indexAction" class="cn.itcast.shop.index.action.IndexAction" scope="prototype"></bean>
- 配置struts.xml
<!-- 配置首页访问的Action -->
<action name="index" class="indexAction">
<result name="index">/WEB-INF/jsp/index.jsp</result>
</action>
用户模块:注册
一:改装页面
- 将页面中能共用的菜单、头部区域提出来(新建一个head.jsp)
- 在原来网页的头部处将head.jsp包入就好:
<@% include file="head.jsp" %>
二:跳转到注册界面
- 点击注册链接进行跳转(添加到一个Action中,从Action中完成页面转向)
- 修改注册的访问路径
${pageContext.request.contextPath}/user_registePage.action
- 编写Action
- 配置applicationContext.xml
<!-- 用户模块的Action -->
<bean id="userAction" class="cn.itcast.shop.user.action.UserAction" scope="prototype"></bean>
- 配置struts.xml
<!-- 配置用户模块的Action -->
<action name="user_*" class="userAction" method="{1}">
<result name="registPage">/WEB-INF/jsp/regist.jsp</result>
</action>
- 修改register.jsp页面:
- 替换css、图片的路径
./css 替换成 ${pageContext.request.contextPath}/css
./images 替换成 ${pageContext.request.contextPath}/images
- 包含head.jsp。当然了,原页面中相应的内容删掉
<@% include file="head.jsp" %>
- 用户注册校验
2.1 前台校验:页面中凡是涉及到提交表单的都要进行验证。
- 统一字段名的name、id(与数据库表中字段、实体类中的字段对应);
- 加 JS。
- AJAX异步校验:验证数据库中是否已存在输入的用户名
- 事件触发:onblur
- 编写AJAX代码:向action中提交:传递username参数
- 编写antion:
* 接收username:模型驱动接收
* 编写实体类:User;
* 映射文件:User.hbm.xml;
* 将实体类与映射文件配置到spring中。
- 编写DAO
* 继承HibernateDaoSupport;
- 在配置中注入sessionFactory
- 编写Service
* 注入UserDao;
* 配置事务管理;
2.2 后台数据校验
- 在Action所在包下创建一个校验的xml
- 创建一个与Action类名-validation.xml(只针对register方法进行校验)
- UserAction-user_regist-validation.xml
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
<validators>
<!-- 校验用户名 -->
<field name="username">
<field-validator type="requiredstring">
<message>用户名不能为空!</message>
</field-validator>
</field>
<!-- 校验用户名 -->
<field name="password">
<field-validator type="requiredstring">
<message>密码不能为空!</message>
</field-validator>
</field>
<!-- 校验邮箱 -->
<field name="email">
<field-validator type="email">
<message>邮箱格式不正确!</message>
</field-validator>
</field>
</validators>
- 在struts.xml中配置INPUT逻辑视图
<!-- 配置用户模块的Action -->
<action name="user_*" class="userAction" method="{1}">
<result name="registPage">/WEB-INF/jsp/register.jsp</result>
<result name="input">/WEB-INF/jsp/register.jsp</result>
</action>
- 在register.jsp页面先引入struts标签
<%@ taglib uri="/struts-tags" prefix="s"%>
- 在相应的位置回显错误提示信息。如密码验证提示如下:
<span><s:fielderror fieldName="password"/></span>
- 用户注册
- 将数据保存到数据库
- 完善user对象
* 用户状态默认为0(未激活状态);
* 激活码采用工具类(UUID)生成(32位激活码);
- 发送激活邮件
- 电子邮箱:邮件服务器上的一块空间;
邮件服务器:类比于web服务器(tomcat),有相应的服务器软件;- 邮件协议:大公司发送和接收服务器分开,也可以都用一个服务器。
SMTP:简单的邮件发送协议;
POP3:接收邮件的一个协议;
- 邮件发送的全过程:理论上需要联网,但是通过搭建在本地的服务器,可以在本地进行邮件的发送。
- 搭建邮箱服务器:
- 安装邮箱服务器(易邮邮箱服务器,就按默认的安装在C盘)
- 修改域名:工具-->服务器设置-->单域名改为:shop.com
- 注册几个内部账号:账号-->新建账号。密码设为55555
- 注册一个商城官方账号:service 密码:55555
- 安装邮件客户端软件(可以接发邮件):避免了网页版session的过期离线问题。
- 流行的有软件有:outlook(微软收费)、foxmail(免费,选用foxmail)
- 配置
* 配置发送邮件服务器:localhost
* 配置接收邮件服务器:localhost
- 编码实现邮件发送
- 用户激活
- 在邮箱客户端中点击激活链接:
- 根据链接中传递的激活码进行查询
* 如果用户不为空:修改用户状态:0 --> 1,向前台传递激活成功消息。
* 如果用户为空:激活码失效或被篡改,向前台传递激活失败消息。
- 用户登录
- 登录页面跳转链接
- 将html编写为login.jsp,并更换引用路径以及菜单头文件
- 修改页面上的登录链接为${pageContext.request.contextPath}/user_loginPage.action
- 编写Action中的方法
- struts.xml中配置页面转向
- 登录功能实现
- 添加login.jsp中的表单action
- 接收用户输入的用户名、密码——模型驱动会自动接收;
- 调用userService,进行查询——参数:用户名、密码、用户状态。
- 登录成功:跳转到首页,并将用户信息显示在首页。
- 登录失败:跳转回登录页面,并将错误信息显示在登录页面。
- 用户退出
- 给链接加action的路径,点击退出链接
- 销毁session
//销毁session
ServletActionContext.getRequest().getSession().invalidate();
- 页面跳转
- 验证码
- 在com.weiwei.shopping.user下编写单独的验证码Action类;
- 在spring、struts配置文件中完成配置;
applicationContext.xml 中:
<!-- 配置验证码Action -->
<bean id="checkImgAction" class="com.weiwei.shopping.user.action.CheckImgAction" scope="prototype"></bean>
struts.xml 中:
<!-- 验证码Action -->
<action name="checkImg" class="checkImgAction"></action>
- 页面中需要的位置显示验证码
<img id="rightcode" class="captchaImage" src="${pageContext.request.contextPath}/checkImg.action" title="点击更换验证码" onclick="ChangeCodeImg()" />
- 编写JS,点击刷新验证码
/*点击更换验证码图片*/
function ChangeCodeImg(){
/*取到验证码图片*/
var codeImg = document.getElementById("rightcode");
/*由于浏览器的缓存,所以需要加上时间戳*/
codeImg.src = "${pageContext.request.contextPath}/checkImg.action?" + new Date().getTime();
}
- 提交到action,在action中进行比较,表单输入的与session中的验证码不相等则相应请求不提交。
/*判断验证码,验证码正确则提交,否则不提交*/
//从session中获取正确的验证码
String rightcode = (String) ServletActionContext.getRequest().getSession().getAttribute("checkcode");
//与表单提交的验证码比较
if(!formcode.equalsIgnoreCase(rightcode)){
//不相等
this.addActionError("验证码输入错误!");
return "loginPage";
}
User existUser = userService.checkLogin(user);
if(existUser == null){
//登录失败
this.addActionError("查无此人,登录失败!");
return "login";
} else{
//登录成功
//将用户的信息存到session中,在进行页面的跳转
ServletActionContext.getRequest().getSession().setAttribute("existUser", existUser);
this.addActionMessage("登录成功!");
return "loginPage";
}