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、總整體層面上了解程序的作用過程

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