JavaWeb企业实战项目(四):订单模块

1、任务总述

1、模型的抽取
2、提交订单
3、查询我的订单
4、订单详情
5、支付功能

2、订单模型抽取

订单: 对本次交易的记录和描述
分析超市小票:
会员ID:1231231
流水号:hrwj_ba_31_sy003_1002
交易时间:2017年3月10日08:41:53
商品名称 商品价格 商品数量 小计
 好日子  15     2    30
 芙蓉王  25     2    50
 黄鹤楼  15     3    45
 大前门  10     1    10
 总金额:135元

设计表,存储小票上的数据
会员id   流水号  交易时间  商品名称  商品价格  商品数量  小计  总金额
1231231  XXX    XXXX   好日子   15      2    30   135
1231231  XXX    XXXX   芙蓉王   25      2    50   135
1231231  XXX    XXXX   黄鹤楼   15      3    45   135
1231231  XXX    XXXX   大前门   10      1    10   135
弊端:数据冗余严重 DB原则:存储最少的数据,办更多的事情

用户表user(uid,username,password,name,birthday,telephone,state,code…)

一个表专注于交易描述,订单表 orders
订单id(流水号)  会员id  交易时间  总金额  收货人姓名  地址  电话  订单状态(1234)
1123      1231231  XXXX   135
1124      1234444  YYYY   200

orders表中的会员id列参照了用户表uid
订单状态:
买家:下单未付款,付款未发货,已发货,签收
卖家: 未付款,发货,未签收,已收货(结束)

一个表专注于每笔订单详细交易情况,订单项orderitem
订单项id  商品id  数量  小计  所在订单编号
001    p003   2   30    1123
002    p005   2   50    1123
003    p101   3   45    1123
004    p220   1   10    1123
005    p334   1   101    1124
006    p556   3   54    1124
007    p445   5   45    1124
orderitem表中的商品id参照了商品表的pid
orderitem表中的所在订单编号参照了订单表的订单id

提交订单原理分析:
用户点击提交订单,将购物车中的数据以订单/订单项形式保存下来,清空购物车
提交订单原理分析
保存订单:
为订单表中插入一行数据,描述本次交易,这行数据部分数据是通过程序赋予,部分数据来自购物车的,部分数据来自session中的用户
oid:UUIDUtils orderTime:new Date(); total: 从购物车获取
state:1 address: null name:null telephone:null uid:从session中的用户获取

保存订单项:
向订单项表中插入数据,描述当前订单的详细的购买信息,部分数据来自于购物车,部分数据需要通过程序赋予
itemid: UUIDUtils quantity:来自于购物车中的购物项 total:来自于购物车中的购物项
pid:来自于购物车上的购物项下商品对象pid oid:来自于当前订单id

提交订单时,订单以及订单项必须同时成功(事务)

3、实现订单模块相关程序

实现目标:
OrderServlet OrderService OrderServiceImp OrderDao OrderDaoImp
Order{ User user, List list=new ArrayList(); …}
OrderItem{ Product product,Order order;}

步骤实现:
1、准备工作
  <a href="${pageContext.request.contextPath}/OrderServlet?method=saveOrder">
2、OrderServlet -> saveOrder

public String saveOrder(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	//确认用户登录状态
	User user = (User)request.getSession().getAttribute("userinfo");
	if(user==null){request.setAttribute("msg", "请在登录之后再下单!");return "/jsp/info.jsp";}
	Cart cart = (Cart)request.getSession().getAttribute("cart");
	//创建订单对象,为订单对象赋值
	Order order = new Order();
	order.setOid(UUIDUtils.getId());
	order.setState(1);
	order.setOrderTime(new Date());
	order.setTotal(cart.getTotalPrice());
	order.setUser(user);
	//遍历购物项的同时,创建订单项
	for (CartItem item: cart.getCartItems()) {
		OrderItem oitem = new OrderItem();
		oitem.setItemid(UUIDUtils.getCode());
		oitem.setQuantity(item.getNum());
		oitem.setTotal(item.getPrice());
		oitem.setProduct(item.getProduct());
		//设置当前的订单项属于哪个订单:程序的角度体检订单项和订单对应关系
		oitem.setOrder(order);
		order.getOrderitem().add(oitem);
	}
	//调用业务层功能:保存订单
	OrderService service = new OrderServiceImpl();
	//将订单数据,用户数据,订单下所有订单项都传递到了service层
	service.saveOrder(order);
	//清空购物车
	cart.clearCart();
	//将订单放入request
	request.setAttribute("order", order);
	//转发/jsp/order_info.jsp
	return "/jsp/order_info.jsp";
}

3、OrderService
  利用事务保存订单,订单项

@Override
public void saveOrder(Order order) {
	OrderDao dao = new OrderDaoImpl();
	try {
		JDBCUtils.startTransaction();
		dao.saveOrderAndItem(order);
		JDBCUtils.commitAndClose();
	} catch (SQLException e) {
		e.printStackTrace();
		JDBCUtils.rollbackAndClose();
	}
}

4、OrderDao

@Override
public void saveOrderAndItem(Order order) throws SQLException {
	String sql1 = "insert into orders values (?,?,?,?,?,?,?,?)";
	String sql2 = "insert into orderitem values (?,?,?,?,?)";
	QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
	Object[] objs1 = {order.getOid(),order.getOrderTime(),order.getTotal(),order.getState(),order.getAddress(),order.getName(),order.getTelephone(),order.getUser().getUid()};
	List<OrderItem> orderitem = order.getOrderitem();
	qr.update(sql1,objs1);
	for (OrderItem item : orderitem) {
		Object[] objs2 = {item.getItemid(),item.getQuantity(),item.getTotal(),item.getProduct().getPid(),order.getOid()};
		qr.update(sql2,objs2);
	}
}

5、/jsp/order_info.jsp
  获取到订单信息(和之前一样,把对象抽取出来)
实现总结: 1、模型的抽取  2、业务层事务

4、订单查询-原理分析

注重软能力的提升

原理如下:
订单查询
步骤实现:
1、准备工作
<a href="${pageContext.request.contextPath}/OrderServlet?method=findMyOrdersWithPage&num=1">我的订单</a>
2、OrderServlet__>findMyOrdersWithPage

public String findMyOrdersWithPage(HttpServletRequest request, HttpServletResponse response) throws Exception {
	//获取当前页数与session中的用户信息
	int curPage = Integer.parseInt(request.getParameter("num"));
	User user = (User)request.getSession().getAttribute("userinfo");
	//创建service对象并执行findMyOrdersWithPage函数
	OrderService service = new OrderServiceImpl();
	//返回的是一个List<Order>对象,保存了多个订单
	PageModel pm = service.findMyOrdersWithPage(user,curPage);
	//作用域保存对象,并转发order_list页面
	request.setAttribute("page", pm);
	return "/jsp/order_list.jsp";
}

3、OrderServiceImp

@Override
public PageModel findMyOrdersWithPage(User user, int curPage) throws Exception{
	//获取总数
	int count = dao.getCount(user);
	System.out.println("商品总述"+count);
	//生成pageModel对象
	PageModel pm = new PageModel(curPage,count,3);
	//关联集合
	List<Order> orders = dao.findMyOrdersWithPage(user,pm.getStartIndex(),pm.getPageSize());
	pm.setList(orders);
	//关联url
	pm.setUrl("OrderServlet?method=findMyOrdersWithPage");
	return pm;
}

4、OrderDaoImp
  *_利用MapListHandler封装多表查询结果
  *_多表查询语句
  *_BeanUtils自动填充数据

@Override
public List<Order> findMyOrdersWithPage(User user, int startIndex, int pageSize) throws Exception {
	//实用limit实现分页查询
	String sql1 = "select * from orders where uid = ? limit ?,?";
	List<Order> orders = qr.query(sql1, new BeanListHandler<Order>(Order.class),user.getUid(),startIndex,pageSize);	
	//foreach循环orders对象
	for (Order o : orders) {
		//获得oid并编写sql2代码,其中查询为多表查询
		String oid = o.getOid();
		String sql2 = "select * from orderitem o,product p where o.pid=p.pid and o.oid = ?";
		//利用MapListHandler封装多表查询结果
		List<Map<String, Object>> map = qr.query(sql2, new MapListHandler(),oid);
		for (Map<String, Object> map2 : map) {
			OrderItem item = new OrderItem();
			Product product = new Product();
			// 1_创建时间类型的转换器
			DateConverter dt = new DateConverter();
			// 2_设置转换的格式
			dt.setPattern("yyyy-MM-dd");
			// 3_注册转换器
			ConvertUtils.register(dt, java.util.Date.class);
			//直接实用populate对对象进行封装,内部利用get函数与map键值匹配并赋值
			BeanUtils.populate(item, map2);
			BeanUtils.populate(product, map2);
			//完成item与product、order与item的关系
			item.setProduct(product);
			item.setOrder(o);
			o.getOrderitem().add(item);
		}
	}
	return orders;
}

5、实现/jsp/order_list.jsp 获取订单信息,完成响应

<strong>我的订单</strong>
<table class="table table-bordered">
	<c:if test="${empty page}">
		<h3>暂无订单</h3>
	</c:if>
	<c:forEach items="${page.list}" var="o">
		<tbody>
			<tr class="success">
				<th colspan="5">
					订单编号:${o.oid} 
					总金额:¥${o.total}
					<c:if test="${o.state==1}">
						<a href="${pageContext.request.contextPath}/OrderServlet?method=findOrderByOid&oid=${o.oid}">付款</a>
					</c:if>
					<c:if test="${o.state==2}">
						未发货
					</c:if>
					<c:if test="${o.state==3}">
						<a href="#">签收</a>
					</c:if>
					<c:if test="${o.state==4}">
						已收货
					</c:if>
				</th>
			</tr>
			<tr class="warning">
				<th>图片</th>
				<th>商品</th>
				<th>价格</th>
				<th>数量</th>
				<th>小计</th>
			</tr>
			<c:forEach items="${o.orderitem}" var="item">
				<tr class="active">
					<td width="60" width="40%">
						<input type="hidden" name="id" value="22">
						<img src="${pageContext.request.contextPath}/${item.product.pimage}" width="70" height="60">
					</td>
					<td width="30%">
						<a target="_blank"> ${item.product.pname}</a>
					</td>
					<td width="20%">
						¥${item.product.shop_price}
					</td>
					<td width="10%">
						${item.quantity}
					</td>
					<td width="15%">
						<span class="subtotal">¥${item.total}</span>
					</td>
				</tr>
			</c:forEach>
		</tbody>
	</c:forEach>
</table>

PS:遍历数据时,2个循环,大循环遍历订单,小循环遍历的是订单上的订单项

5、实现订单详情查询

原理如下:
订单详情
步骤实现:
1、准备工作 order_list.jsp 修改连接
<a href="${pageContext.request.contextPath}/OrderServlet?method=findOrderByOid&oid=${o.oid}">付款</a>
2、OrderServlet -> findOrderByOid

public String findOrderByOid(HttpServletRequest request, HttpServletResponse response) throws Exception {
	//获取list页面传过来的oid参数
	String oid = request.getParameter("oid");
	//调用业务层功能:根据订单编号查询订单信息
	OrderService service = new OrderServiceImpl();
	Order order = service.findOrderByOid(oid);
	//将Order放入request作用域中并执行转发
	request.setAttribute("order", order);
	return "/jsp/order_info.jsp";
}

3、OrderService

return dao.findOrderByOid(oid);

4、OrderDaoImp
思路:
  根据订单oid查询当前订单
  根据订单oid查询订单下所有的订单项以及订单项关联的商品

@Override
public Order findOrderByOid(String oid) throws Exception {
	String sql = "select * from orders where oid = ?";
	Order order = qr.query(sql, new BeanHandler<Order>(Order.class),oid);
	sql = "select * from orderitem o,product p where o.pid=p.pid and o.oid = ?";
	//利用MapListHandler封装多表查询结果
	List<Map<String, Object>> map = qr.query(sql, new MapListHandler(),oid);
	for (Map<String, Object> map2 : map) {
		OrderItem item = new OrderItem();
		Product product = new Product();
		// 1_创建时间类型的转换器
		DateConverter dt = new DateConverter();
		// 2_设置转换的格式
		dt.setPattern("yyyy-MM-dd");
		// 3_注册转换器
		ConvertUtils.register(dt, java.util.Date.class);
		//直接实用populate对对象进行封装,内部利用get函数与map键值匹配并赋值
		BeanUtils.populate(item, map2);
		BeanUtils.populate(product, map2);
		//完成item与product、order与item的关系
		item.setProduct(product);
		item.setOrder(order);
		order.getOrderitem().add(item);
	}
	return order;
}

5、/jsp/order_info.jsp
开发中:多个功能对应同一个JSP页面,为了提高代码复用率,所有的功能在向同一个
    JSP页面转发的时候,向request存入相同的数据(属性名一致)

6、支付功能-原理分析

基础概念:
银行接口:
  优点:资金没有延时
  缺点:银行API发生变化,支付功能需要更改
第三方支付api:
  缺点:资金延时,收费
  优点:银行API发生变化,支付功能不需要更改
支付数据执行过程:
支付过程
如何保证数据传输有效性:
张三和李四传输数据:
 原文:ABCDE
 算法:对原文中的每个字符的ASC码增加一个数字(公开)
 秘钥:增加的3(只有张三和李四知道)
 密文:CDEFG
张三向李四传递数据:ABCDE&CDEFG
李四获取到张三的数据之后:ABCDE利用算法和秘钥3对原文再次加密得到密文
比较得到的密文和获取到的密文是否一致,一致数据是合法的

加密算法:对称加密,非对称加密
对称加密:原文<----->密文
非对称加密:原文<----->密文

A公司实现电商项目,A公司需要拿着公司营业执照,银行账户各种资质证明,去易宝支付申请使用权限,易宝支付审核之后为A公司分配商户编号,秘钥

7、实现支付功能

原理如下:
支付功能
步骤实现:
1、准备工作 /jsp/order_info.jsp
  //设置form表单(method,action,id)
  //设置form表单 input 标签的name属性 address name telephone
  //设置隐藏域 传递订单oid
  都是像以前的常规做法,不贴代码
2、OrderServlet___>payOrder
  //获取订单oid,收货人地址,姓名,电话,银行
  //更新订单上收货人的地址,姓名,电话
  //向易宝支付发送参数
  由于易宝支付已经挂了,想注册一个商户都注册不了,所以就直接完成支付

public String payOrder(HttpServletRequest request, HttpServletResponse response) throws Exception {
	//获取订单oid、收货人地址/电话/姓名/银行
	String oid = request.getParameter("oid");
	String name = request.getParameter("name");
	String telephone = request.getParameter("telephone");
	String address = request.getParameter("address");
	String pd_FrpId = request.getParameter("pd_FrpId");
	//调用业务层功能:更新订单上收货人的地址、姓名、电话
	OrderService service = new OrderServiceImpl();
	Order order = service.findOrderByOid(oid);
	order.setAddress(address);
	order.setTelephone(telephone);
	order.setName(name);
	order.setState(2);
	service.updateOrder(order);
	request.setAttribute("msg", "支付成功!订单号:" + oid + "&emsp;金额:" + order.getTotal());
	return "/jsp/info.jsp";
	
	/** 向易宝支付发送参数 **/
	/*//把付款所需要的参数准备好:
	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 = "";
	//接受响应参数的Servlet
	String 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("https://www.yeepay.com/app-merchant-proxy/node?");
	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());
	// 使用重定向:
	response.sendRedirect(sb.toString());*/
}

3、OrderServlet___>callBack
  //接收响应会的数据
  //确保数据有效性
  //更新订单状态
  //向request放入提示信息
  //转发到/jsp/info.jsp

/** 易宝支付GG,此功能作废!*/
public void callBack(HttpServletRequest request, HttpServletResponse response) throws Exception {
	// 验证请求来源和数据有效性
	// 阅读支付结果参数说明
	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");
	// hmac
	String 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")) {
			// 浏览器重定向
			response.setContentType("text/html;charset=utf-8");
			OrderService service = new OrderServiceImpl();
			Order order = service.findOrderByOid(r6_Order);
			order.setState(2);
			service.updateOrder(order);
			request.setAttribute("msg", "支付成功!订单号:" + r6_Order + "金额:" + r3_Amt);
		} else if (r9_BType.equals("2")) {
			// 修改订单状态:
			// 服务器点对点,来自于易宝的通知
			System.out.println("收到易宝通知,修改订单状态!");//
			// 回复给易宝success,如果不回复,易宝会一直通知
			response.getWriter().print("success");
		}
	} else {
		throw new RuntimeException("数据被篡改!");
	}
}

总结:
1、熟练掌握sql语句查询与更新
2、熟悉调试的方法,真正开发项目的时候是不会有源码对比参考的
3、总整体层面上了解程序的作用过程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章