文章目录
- 1、任务总述
- 2、订单模型抽取
- 3、实现订单模块相关程序
- 4、订单查询-原理分析
- 5、实现订单详情查询
- 6、支付功能-原理分析
- 7、实现支付功能
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、总整体层面上了解程序的作用过程