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