模块"/>
订单模块
抽取订单模型
分析订单组成
会员ID:
流水号:
交易事件:
商品名称 商品价格 商品数量 小计
.
.
.
总金额:
设计表,存储小票上的数据
- 创建订单表
CREATE TABLE `orders` (`oid` VARCHAR(32) NOT NULL,`ordertime` DATETIME DEFAULT NULL, #下单时间`total` DOUBLE DEFAULT NULL, #总价`state` INT(11) DEFAULT NULL, #订单状态:1=未付款;2=已付款,未发货;3=已发货,没收货;4=收货,订单结束`address` VARCHAR(30) DEFAULT NULL, #收获地址`name` VARCHAR(20) DEFAULT NULL, #收获人`telephone` VARCHAR(20) DEFAULT NULL, #收货人电话`uid` VARCHAR(32) DEFAULT NULL,PRIMARY KEY (`oid`),KEY `order_fk_0001` (`uid`),CONSTRAINT `order_fk_0001` FOREIGN KEY (`uid`) REFERENCES `user` (`uid`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
- 创建订单项表
CREATE TABLE `orderitem` (`itemid` VARCHAR(32) NOT NULL,`quantity` INT(11) DEFAULT NULL, #购买数量`total` DOUBLE DEFAULT NULL, #小计`pid` VARCHAR(32) DEFAULT NULL, #购买商品的id`oid` VARCHAR(32) DEFAULT NULL, #订单项所在订单idPRIMARY KEY (`itemid`),KEY `order_item_fk_0001` (`pid`),KEY `order_item_fk_0002` (`oid`),CONSTRAINT `order_item_fk_0001` FOREIGN KEY (`pid`) REFERENCES `product` (`pid`),CONSTRAINT `order_item_fk_0002` FOREIGN KEY (`oid`) REFERENCES `orders` (`oid`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
提交订单
需求
用户点击提交订单,将购物车中的数据以订单/订单项形式保存下来,清空购物车
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bdivx073-1607676523969)(提交订单开发流程.png)]
-
保存订单:为订单表中插入一行数据,描述本次交易,这行数据部分数据是通过程序赋予,部分数据来自购物车的,部分数据来自session中的用户。
oid:UUIDUtilsoderTime:new Date();total:从购物车中获取state:1address:nullname:nulltelephone:nulluid:从session中的用户获取
-
保存订单项:向订单项表中插入数据,描述当前订单的详细的购买信息,部分数据来自购物车,部分数据需要通过程序赋予。
itemid:UUIDUtilsquantity:来自于订单中的购物项total:来自于购物车中的购物项pid:来自于购物车上的购物项下商品对象pidoid:来自于当前订单的id
提交订单时,订单以及订单项必须同时成功(事务)
创建订单对象
- 创建一个user对象而不是user对象的属性uid。
- 程序对象和对象发生关系,而不是对象和对象的属性发生关系
- 设计Order目的:让order携带订单上的数据向service,dao传递,user对象是可以携带更多的数据
- private List list = new ArrayList();程序中体现订单对象和订单项之间关系,我们再项目中的部分功能中有类似的需求:查询订单的同时还需要获取订单下所有的订单项
public class Order {private String oid; //订单编号private Date ordertime; //下单时间private double total; //总计private int state; //状态private String address; //收货人地址private String name; //收货人姓名private String telephone; //收货人电话// private String uid;// 1_程序对象和对象发生关系,而不是对象和对象的属性发生关系// 2_设计Order目的:让order携带订单上的数据向service,dao传递,user对象是可以携带更多的数据private User user;// 程序中体现订单对象和订单项之间关系,我们再项目中的部分功能中有类似的需求:查询订单的同时还需要获取订单下所有的订单项private List<OrderItem> list = new ArrayList<OrderItem>();public String getOid() {return oid;}public void setOid(String oid) {this.oid = oid;}public Date getOrdertime() {return ordertime;}public void setOrdertime(Date ordertime) {this.ordertime = ordertime;}public double getTotal() {return total;}public void setTotal(double total) {this.total = total;}public int getState() {return state;}public void setState(int state) {this.state = state;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getTelephone() {return telephone;}public void setTelephone(String telephone) {this.telephone = telephone;}public User getUser() {return user;}public void setUser(User user) {this.user = user;}public List<OrderItem> getList() {return list;}public void setList(List<OrderItem> list) {this.list = list;}}
创建订单项对象
创建一个product对象而不是product对象的属性pid;
创建一个order对象而不是product对象的属性oid。
- 对象对应对象
- product,order携带更多的数据
public class OrderItem {private String itemid; //idprivate int quantity; //数量private double total; //小计//1_对象对应对象//2_product,order携带更多的数据private Product product;private Order order; public String getItemid() {return itemid;}public void setItemid(String itemid) {this.itemid = itemid;}public int getQuantity() {return quantity;}public void setQuantity(int quantity) {this.quantity = quantity;}public double getTotal() {return total;}public void setTotal(double total) {this.total = total;}public Product getProduct() {return product;}public void setProduct(Product product) {this.product = product;}public Order getOrder() {return order;}public void setOrder(Order order) {this.order = order;}}
保存订单
- 客户端点击提交订单,向服务器发起请求
<a href="${pageContext.request.contextPath}/orderServlet?method=saveOrder"><%--提交表单 --%><input type="button" width="100" value="提交订单" name="submit" border="0"style="background: url('${pageContext.request.contextPath}/img/register.gif') no-repeat scroll 0 0 rgba(0, 0, 0, 0);height:35px;width:100px;color:white;">
</a>
- OrderServlet下编写saveOrder方法
public String saveOrder(HttpServletRequest req, HttpServletResponse resp) throws Exception {//确认用户登录状态User user = (User) req.getSession().getAttribute("loginUser");if (user == null) {req.setAttribute("msg", "请登陆后再下单");return "/jsp/info.jsp";}//获取购物车Cart cart = (Cart) req.getSession().getAttribute("cart");//创建订单对象,为订单对象赋值Order order = new Order();order.setOid(UUIDUtils.getCode());order.setOrdertime(new Date());order.setTotal(cart.getTotal());order.setState(1);order.setUser(user);//遍历购物项的同时,创建订单项for (CartItem item : cart.getCartItems()) {OrderItem orderItem = new OrderItem();orderItem.setItemid(UUIDUtils.getCode());orderItem.setQuantity(item.getNum());orderItem.setTotal(item.getSubTotal());orderItem.setProduct(item.getProduct());//设置当前的订单项属于哪个订单:订单项和订单的对应关系orderItem.setOrder(order);order.getList().add(orderItem);}//调用业务层功能OrderService orderService = new OrderServiceImp();//将订单数据,用户的数据,订单下所有的订单项都传递到了service层orderService.saveOrder(order);//清空购物车cart.clearCart();//将订单放入requestreq.setAttribute("order",order);//转发/jsp/order_info.jspreturn "/jsp/order_info.jsp";
}
- 编写业务层,保存订单功能
public void saveOrder(Order order) throws SQLException {//保存订单和订单下所有的订单项(同时成功,失败)/*try {JDBCUtils.startTransaction();OrderDao orderDao=new OrderDaoImp();orderDao.saveOrder(order);for(OrderItem item:order.getList()){orderDao.saveOrderItem(item);}JDBCUtilsmitAndClose();} catch (Exception e) {JDBCUtils.rollbackAndClose();}*/Connection conn = null;try {//获取连接conn = JDBCUtils.getConnection();//开启事务conn.setAutoCommit(false);//保存订单orderDao.saveOrder(conn, order);//保存订单项for (OrderItem item : order.getList()) {orderDao.saveOrderItem(conn, item);}//提交connmit();} catch (Exception e) {//回滚conn.rollback();}
}
- 编写dao层,插入数据到订单表和订单项表
@Override
public void saveOrder(Connection conn, Order order) {String sql="INSERT INTO orders VALUES(?,?,?,?,?,?,?,?)";Object[] params={order.getOid(),order.getOrdertime(),order.getTotal(),order.getState(),order.getAddress(),order.getName(),order.getTelephone(),order.getUser().getUid()};template.update(sql,params);
}
@Override
public void saveOrderItem(Connection conn, OrderItem item) {String sql="INSERT INTO orderitem VALUES(?,?,?,?,?)";Object[] params={item.getItemid(),item.getQuantity(),item.getTotal(),item.getProduct().getPid(),item.getOrder().getOid()};template.update(sql,params);
}
- 编写订单详情页面order_info.jsp
<div class="row"><div style="margin:0 auto;margin-top:10px;width:950px;"><strong>订单详情</strong><table class="table table-bordered"><tbody><tr class="warning"><th colspan="5">订单编号:${order.oid}</th></tr><tr class="warning"><th>图片</th><th>商品</th><th>价格</th><th>数量</th><th>小计</th></tr><c:forEach items="${order.list}" var="orderItem"><tr class="active"><td width="60" width="40%"><input type="hidden" name="id" value="22"><img src="${pageContext.request.contextPath}/${orderItem.product.pimage}" width="70"height="60"></td><td width="30%"><a target="_blank">${orderItem.product.pname}</a></td><td width="20%">¥${orderItem.product.shop_price}</td><td width="10%">${orderItem.quantity}</td><td width="15%"><span class="subtotal">¥${orderItem.total}</span></td></tr></c:forEach></tbody></table></div><div style="text-align:right;margin-right:120px;">商品金额: <strong style="color:#ff6600;">¥${order.total}元</strong></div></div>
查询我的订单
### 编写findMyOrdersWithPage方法
首先获取用户信息,调用业务层功能查询当前用户的订单信息
public String findMyOrdersWithPage(HttpServletRequest req, HttpServletResponse resp) throws Exception {//获取用户信息User user = (User) req.getSession().getAttribute("loginUser");//获取当前页int curNum = Integer.parseInt(req.getParameter("num"));//调用业务层功能:查询当前用户订单信息,返回PageModelOrderService orderService = new OrderServiceImp();//SELECT * FROM orders WHERE uid=? limit ? , ?//PageModel:1_分页参数 2_url 3_当前用户的当前页的订单(集合) ,每笔订单上对应的订单项,以及订单项对应的商品信息PageModel pm = orderService.findMyOrdersWithPage(user, curNum);//将PageModel放入requestreq.setAttribute("page", pm);//转发到/jsp/order_list.jspreturn "/jsp/order_list.jsp";}
编写业务层
public PageModel findMyOrdersWithPage(User user, int curNum) throws Exception {//1_创建PageModel对象,目的:计算并且携带分页参数//select count(*) from orders where uid=?int totalRecords = orderDao.getTotalRecords(user);PageModel pm = new PageModel(curNum, totalRecords, 3);//2_关联集合 select * from orders where uid=? limit ? ,?List list = orderDao.findMyOrdersWithPage(user, pm.getStartIndex(), pm.getPageSize());pm.setList(list);//3_关联urlpm.setUrl("orderServlet?method=findMyOrdersWithPage");return pm;
}
编写dao层,查询数据库
遍历所有订单,并获取每笔订单下的订单项
public List findMyOrdersWithPage(User user, int startIndex, int pageSize) throws Exception {String sql = "select * from orders where uid=? limit ? , ?";List<Order> list = template.query(sql, new BeanPropertyRowMapper<>(Order.class), user.getUid(), startIndex, pageSize);//遍历所有订单for (Order order : list) {//获取到每笔订单oid 查询每笔订单下的订单项以及订单项对应的商品信息String oid = order.getOid();sql = "select * from orderItem o ,product p where o.pid=p.pid and oid=?";List<Map<String, Object>> list01 = template.queryForList(sql, oid);//遍历listfor (Map<String, Object> map : list01) {OrderItem orderItem = new OrderItem();Product product = new Product();// 由于BeanUtils将字符串"1992-3-3"向user对象的setBithday();方法传递参数有问题,手动向BeanUtils注册一个时间类型转换器// 1_创建时间类型的转换器DateConverter dt = new DateConverter();// 2_设置转换的格式dt.setPattern("yyyy-MM-dd");// 3_注册转换器ConvertUtils.register(dt, java.util.Date.class);//将map中属于orderItem的数据自动填充到orderItem对象上BeanUtils.populate(orderItem, map);//将map中属于product的数据自动填充到product对象上BeanUtils.populate(product, map);//让每个订单项和商品发生关联关系orderItem.setProduct(product);//将每个订单项存入订单下的集合中order.getList().add(orderItem);}}return list;
}
### 编写订单列表页面/jsp/order_list.jsp
<div class="container"><c:if test="${empty page.list}"><div>暂无数据</div></c:if><c:if test="${not empty page.list }"><div class="row"><div style="margin:0 auto; margin-top:10px;width:950px;"><strong>我的订单</strong><table class="table table-bordered"><c:forEach items="${page.list}" var="order"><tbody><tr class="success"><th colspan="5">订单编号:${order.oid} 金额:${order.total}<c:if test="${order.state==1}"><a href="${pageContext.request.contextPath}/orderServlet?method=findOrderByOid&oid=${order.oid}">付款</a></c:if><c:if test="${order.state==2}">待发货</c:if><c:if test="${order.state==3}">在路上</c:if><c:if test="${order.state==4}">结束</c:if></th></tr><tr class="warning"><th>图片</th><th>商品</th><th>价格</th><th>数量</th><th>小计</th></tr><c:forEach items="${order.list}" var="orderItem"><tr class="active"><td width="60" width="40%"><input type="hidden" name="id" value="22"><img src="${pageContext.request.contextPath}/${orderItem.product.pimage}" width="70"height="60"></td><td width="30%"><a target="_blank">${orderItem.product.pname}</a></td><td width="20%">¥${orderItem.product.shop_price}</td><td width="10%">${orderItem.quantity}</td><td width="15%"><span class="subtotal">¥${orderItem.total}</span></td></tr></c:forEach></tbody></c:forEach></table></div></div><%@ include file="/jsp/pageFile.jsp" %></c:if>
</div>
订单详情
客户端点击付款,跳转至订单详情页面
编写findOrderByOid方法
public String findOrderByOid(HttpServletRequest req, HttpServletResponse resp) throws Exception {//获取到订单oidString oid = req.getParameter("oid");//调用业务层功能:根据订单编号查询订单信息OrderService OrderService = new OrderServiceImp();Order order = OrderService.findOrderByOid(oid);// 将订单放入requestreq.setAttribute("order", order);// 转发到/jsp/order_info.jspreturn "/jsp/order_info.jsp";
}
编写业务层
根据订单编号查询订单信息
public Order findOrderByOid(String oid) throws Exception {return orderDao.findOrderByOid(oid);
}
编写dao层
public Order findOrderByOid(String oid) throws Exception {String sql = "select * from orders where oid= ?";Order order = (Order) template.queryForObject(sql, new BeanPropertyRowMapper<>(Order.class), oid);//根据订单id查询订单下所有的订单项以及订单项对应的商品信息sql = "select * from orderitem o, product p where o.pid=p.pid and oid=?";List<Map<String, Object>> list02 = template.queryForList(sql, oid);//遍历listfor (Map<String, Object> map : list02) {OrderItem orderItem = new OrderItem();Product product = new Product();// 由于BeanUtils将字符串"1992-3-3"向user对象的setBithday();方法传递参数有问题,手动向BeanUtils注册一个时间类型转换器// 1_创建时间类型的转换器DateConverter dt = new DateConverter();// 2_设置转换的格式dt.setPattern("yyyy-MM-dd");// 3_注册转换器ConvertUtils.register(dt, java.util.Date.class);//将map中属于orderItem的数据自动填充到orderItem对象上BeanUtils.populate(orderItem, map);//将map中属于product的数据自动填充到product对象上BeanUtils.populate(product, map);//让每个订单项和商品发生关联关系orderItem.setProduct(product);//将每个订单项存入订单下的集合中order.getList().add(orderItem);}return order;
}
支付功能
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tq6f1Hro-1607676523973)(支付开发流程.png)]
准备工作 /jsp/order_info.jsp
设置form表单(method,action,id)
<form class="form-horizontal" style="margin-top:5px;margin-left:150px;" id="orderForm" method="post"action="${pageContext.request.contextPath}/orderServlet?method=payOrder"><a href="javascript:document.getElementById('orderForm').submit();"><img src="${pageContext.request.contextPath}/img/finalbutton.gif" width="204" height="51" border="0"/></a>
</form>
设置form表单input标签,设置隐藏域,传递订单oid。
<div class="form-group"><label for="username" class="col-sm-1 control-label">地址</label><div class="col-sm-5"><input type="text" class="form-control" id="username" placeholder="请输入收货地址" name="address"></div>
</div>
<div class="form-group"><label for="inputPassword3" class="col-sm-1 control-label">收货人</label><div class="col-sm-5"><input type="text" class="form-control" id="inputPassword3" placeholder="请输收货人" name="name"></div>
</div>
<div class="form-group"><label for="confirmpwd" class="col-sm-1 control-label">电话</label><div class="col-sm-5"><input type="text" class="form-control" id="confirmpwd" placeholder="请输入联系方式" name="telephone"><input type="hidden" name="oid" value="${order.oid }"/></div>
</div>
编写OrderServlet下的payOrder方法
获取订单oid,收货人地址,姓名电话
更新订单上的收货人地址,姓名电话
向支付宝发送参数
public String payOrder(HttpServletRequest req, HttpServletResponse resp) throws Exception {//获取订单oid,收货人地址,姓名,电话,银行String oid=req.getParameter("oid");String address=req.getParameter("address");String name=req.getParameter("name");String telephone=req.getParameter("telephone");String pd_FrpId=req.getParameter("pd_FrpId");//更新订单上收货人的地址,姓名,电话OrderService OrderService=new OrderServiceImp();Order order=OrderService.findOrderByOid(oid);order.setName(name);order.setTelephone(telephone);order.setAddress(address);OrderService.updateOrder(order);//向易宝支付发送参数// 把付款所需要的参数准备好:String p0_Cmd = "Buy";//商户编号String p1_MerId = "10001126856";//订单编号String p2_Order = oid;//金额String p3_Amt = "0.01";String p4_Cur = "CNY";String p5_Pid = "";String p6_Pcat = "";String p7_Pdesc = "";//接受响应参数的ServletString p8_Url = "http://localhost:8080/store_v5/OrderServlet?method=callBack";String p9_SAF = "";String pa_MP = "";String pr_NeedResponse = "1";//公司的秘钥String keyValue = "69cl522AV6q613Ii4W6u8K6XuW8vM1N6bFgyv769220IuYe9u37N4y7rI4Pl";//调用易宝的加密算法,对所有数据进行加密,返回电子签名String hmac = PaymentUtil.buildHmac(p0_Cmd, p1_MerId, p2_Order, p3_Amt, p4_Cur, p5_Pid, p6_Pcat, p7_Pdesc, p8_Url, p9_SAF, pa_MP, pd_FrpId, pr_NeedResponse, keyValue);StringBuffer sb = new StringBuffer("?");sb.append("p0_Cmd=").append(p0_Cmd).append("&");sb.append("p1_MerId=").append(p1_MerId).append("&");sb.append("p2_Order=").append(p2_Order).append("&");sb.append("p3_Amt=").append(p3_Amt).append("&");sb.append("p4_Cur=").append(p4_Cur).append("&");sb.append("p5_Pid=").append(p5_Pid).append("&");sb.append("p6_Pcat=").append(p6_Pcat).append("&");sb.append("p7_Pdesc=").append(p7_Pdesc).append("&");sb.append("p8_Url=").append(p8_Url).append("&");sb.append("p9_SAF=").append(p9_SAF).append("&");sb.append("pa_MP=").append(pa_MP).append("&");sb.append("pd_FrpId=").append(pd_FrpId).append("&");sb.append("pr_NeedResponse=").append(pr_NeedResponse).append("&");sb.append("hmac=").append(hmac);System.out.println(sb.toString());//// 使用重定向:resp.sendRedirect(sb.toString());return null;
}
编写OrderServlet下的callBack方法
接收易宝支付的数据
验证请求来源和数据有效性
阅读支付结果参数说明
public String callBack(HttpServletRequest request, HttpServletResponse resp) throws Exception {//接收易宝支付的数据// 验证请求来源和数据有效性// 阅读支付结果参数说明// System.out.println("==============================================");String p1_MerId = request.getParameter("p1_MerId");String r0_Cmd = request.getParameter("r0_Cmd");String r1_Code = request.getParameter("r1_Code");String r2_TrxId = request.getParameter("r2_TrxId");String r3_Amt = request.getParameter("r3_Amt");String r4_Cur = request.getParameter("r4_Cur");String r5_Pid = request.getParameter("r5_Pid");String r6_Order = request.getParameter("r6_Order");String r7_Uid = request.getParameter("r7_Uid");String r8_MP = request.getParameter("r8_MP");String r9_BType = request.getParameter("r9_BType");String rb_BankId = request.getParameter("rb_BankId");String ro_BankOrderId = request.getParameter("ro_BankOrderId");String rp_PayDate = request.getParameter("rp_PayDate");String rq_CardNo = request.getParameter("rq_CardNo");String ru_Trxtime = request.getParameter("ru_Trxtime");// hmacString hmac = request.getParameter("hmac");// 利用本地密钥和加密算法 加密数据String keyValue = "69cl522AV6q613Ii4W6u8K6XuW8vM1N6bFgyv769220IuYe9u37N4y7rI4Pl";//保证数据合法性boolean isValid = PaymentUtil.verifyCallback(hmac, p1_MerId, r0_Cmd,r1_Code, r2_TrxId, r3_Amt, r4_Cur, r5_Pid, r6_Order, r7_Uid,r8_MP, r9_BType, keyValue);if (isValid) {// 有效if (r9_BType.equals("1")) {// 浏览器重定向//如果支付成功,更新订单状态OrderService OrderService=new OrderServiceImp();Order order=OrderService.findOrderByOid(r6_Order);order.setState(2);OrderService.updateOrder(order);//向request放入提示信息request.setAttribute("msg", "支付成功!订单号:" + r6_Order + "金额:" + r3_Amt);//转发到/jsp/info.jspreturn "/jsp/info.jsp";} else if (r9_BType.equals("2")) {// 修改订单状态:// 服务器点对点,来自于易宝的通知System.out.println("收到易宝通知,修改订单状态!");//// 回复给易宝success,如果不回复,易宝会一直通知resp.getWriter().print("success");}} else {throw new RuntimeException("数据被篡改!");}return null;}
权限过滤器
某些功能需要登录才可访问。
public class PriviledgeFilter implements Filter {public PriviledgeFilter() {}public void destroy() {}public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest myReq = (HttpServletRequest)request;//判断当前的session中是否存在已经登录成功的用户User user = (User)myReq.getSession().getAttribute("loginUser");if(null!=user){//如果存在,放行chain.doFilter(request, response);}else{//如果不存在,转入到提示页面myReq.setAttribute("msg", "请用户登录之后再去访问");//转入到提示页面myReq.getRequestDispatcher("/jsp/info.jsp").forward(request, response);} }public void init(FilterConfig fConfig) throws ServletException {}}
配置过滤器路径
<filter><display-name>PriviledgeFilter</display-name><filter-name>PriviledgeFilter</filter-name><filter-class>web.filter.PriviledgeFilter</filter-class>
</filter>
<filter-mapping><filter-name>PriviledgeFilter</filter-name><url-pattern>/jsp/cart.jsp</url-pattern><url-pattern>/jsp/order_info.jsp</url-pattern><url-pattern>/jsp/order_list.jsp</url-pattern>
</filter-mapping>
更多推荐
订单模块
发布评论