【左京淳的JAVA WEB学习笔记】第十四章 付款处理

新建PayMoneySvl
付款後清空购物车并更新帐户余额
为避免重复扣款,重定向到付款成功页面。

@WebServlet("/user/PayMoneySvl")
public class PayMoneySvl extends HttpServlet {
  public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //取得使用者
    TUser user = (TUser)request.getSession().getAttribute("user");
    //取得购买总额
    String value = request.getParameter("allMoney");
    double allMoney = Double.parseDouble(value);
    //若余额足够
    if(user.getAccount() >= allMoney){
      //执行扣款及清理购物车
      UserBiz biz = new UserBiz();
      Map<String,Integer> car = (Map<String,Integer>)request.getSession().getAttribute("ShopCar");
      try {
        //处理订单,传入使用者名称、结帐总额、购物清单
        biz.buyBooks(user.getUname(),allMoney,car);
        //付款成功後清除购物车,并更新帐户余额
        car.clear();
        user.setAccount(user.getAccount() - allMoney);
        //重定向至付款成功页面
        String path = request.getContextPath();
          String basePath = request.getScheme() + "://" + request.getServerName()  
          + ":" + request.getServerPort() + path + "/";
          response.sendRedirect(basePath + "PayOkSvl?allMoney=" + allMoney);
      }catch(Exception e) {
        request.setAttribute("msg", "网路异常,请和管理员联系");
        request.getRequestDispatcher("/error.jsp").forward(request, response);
      }
    }else {
      request.setAttribute("msg", "余额不足");
      request.getRequestDispatcher("/error.jsp").forward(request, response);
    }
  }

在UserBiz新增buyBooks()方法

  public void buyBooks(String uname, double allMoney, Map<String, Integer> shopCar) {
    IUserDao dao = new UserDaoMysql();
    try {
      //扣款时若其中一个动作失败,就全部回复。
      dao.beginTransaction();
      //因为是扣款,所以金额用负数
      dao.updateUserAccount(uname,-allMoney);
      dao.addBuyRecord(uname,allMoney,shopCar);
      dao.commit();
    }catch(Exception e) {
      dao.rollback();
      Log.logger.error(e.getMessage(),e);
      throw e;
    }finally {
      dao.closeConnection();
    }
  }

在UserDaoMysql新增updateUserAccount()方法,实现扣款。

  public void updateUserAccount(String uname,double money) throws Exception{
    String sql = "update tuser set account = account + ? where uname = ?";
    this.openConnection();
    PreparedStatement ps = this.connection.prepareStatement(sql);
    ps.setDouble(1, money);
    ps.setString(2, uname);
    ps.executeUpdate();
    ps.close();
  }

在UserDaoMysql新增addBuyRecord()方法,生成订单。

  public void addBuyRecord(String uname,double allmoney,Map<String,Integer>ShopCar) throws Exception{
    String orderNo = OrderUtil.createNewOrderNo();
    String sql = "insert into tbuyrecord values(?,?,?,?)";
    this.openConnection();
    PreparedStatement ps = this.connection.prepareStatement(sql);
    ps.setString(1, orderNo);
    ps.setString(2, uname);
    ps.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
    ps.setDouble(4, allmoney);
    ps.executeUpdate();
    ps.close();
  }

在UserDaoMysql新增addBuyDetail()方法,添加订单明细。

  public void addBuyRecord(String uname,double allmoney,Map<String,Integer>shopCar) throws Exception{
    //新增订单
    String orderNo = OrderUtil.createNewOrderNo();
    String sql = "insert into tbuyrecord values(?,?,?,?)";
    this.openConnection();
    PreparedStatement ps = this.connection.prepareStatement(sql);
    ps.setString(1, orderNo);
    ps.setString(2, uname);
    ps.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
    ps.setDouble(4, allmoney);
    ps.executeUpdate();
    //更新DB内商品资料
    Set <String> isbns = shopCar.keySet();
    for(String isbn:isbns) {
      //把商品条码、购买数量、订单编号等资讯整理进TBuyDetail物件中。
      TBuyDetail detail = new TBuyDetail();
      detail.setIsbn(isbn);
      detail.setBuycount(shopCar.get(isbn));
      detail.setBuyid(orderNo);
      //使用TBuyDetail物件对DB资料进行更新。
      addBuyDetail(detail);
    }
    ps.close();
  }
  
  private void addBuyDetail(TBuyDetail detail) throws Exception{  
    //取得tbuydetail表取得autoId,再加入其他参数(商品号、订单号、数量)塞回去。
    String sql = "insert into tbuydetail values((select * from (select IFNULL(max(autoid),0) + 1 from tbuydetail) t1),?,?,?)";
    this.openConnection();
    PreparedStatement ps = this.connection.prepareStatement(sql);
    ps.setString(1, detail.getIsbn());
    ps.setString(2, detail.getBuyid());
    ps.setInt(3, detail.getBuycount());
    ps.executeUpdate();
    ps.close();
    
    //用BookDao.updateBookCount()扣除库存
    //为了能够完整进行transaction,把同一个connection丢给BookDao继续使用
    BookDaoMysql bookDao = new BookDaoMysql();
    bookDao.setConnection(this.getConnection());
    bookDao.updateBookCount(detail.getIsbn(),-detail.getBuycount());
  }
  
  public void updateBookCount(String isbn, int bookCount) throws Exception{
    String sql = "update tbook set bkcount = bkcount + ? where isbn = ?";
    this.openConnection(); 
    PreparedStatement ps = this.connection.prepareStatement(sql);
    ps.setInt(1, bookCount);
    ps.setString(1, isbn);
    ps.executeUpdate();
    ps.close();
  }

流程整理

从页面取得: 使用者名称、结帐金额、购物车,传给後台: PayMoneySvl
呼叫biz.buyBooks()执行transaction任务
1.执行扣款
2.使用addBuyRecord()新增订单,并更新库存
2-1使用userDao将资料分别填入订单表及订单细目表
订单表(订单号、客户名、时间、总额)
订单细目表(autoId、商品号、订单号、数量)
2-2
使用bookDao.updateBookCount()更新库存。与userDao使用同一个connection。
3.commit()

新建PayOkSvl,处理付款成功後的重定向

@WebServlet("/PayOkSvl")
public class PayOkSvl extends HttpServlet {
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    request.getRequestDispatcher("/WEB-INF/main/PayOk.jsp").forward(request, response);
  }
}

新建PayOk.jsp,显示付款成功讯息。

  <table align="center" width=60%>
    <tr>
      <td height="180"></td>
      <td style="color:black;font-size:18px">
        付款成功!付款人:${user.uname }<br>
        付款金额:<fmt:formatNumber value="${allMoney }" pattern="#.00" type="number"></fmt:formatNumber>
        <p style="color:red;font-size:30px">我们会尽快为您寄送商品</p>
      </td>
    </tr>
    <tr><td height="80"></td></tr>
    <tr>
      <td colspan="2" align="center">
        <a href="<%=basePath%>MainSvl">返回首页</a>
      </td>
    </tr>
  </table>

付款异常处理

在transaction任务内的任何一步骤添加throw new RuntimeException("异常测试");
即可在DB进行数据确认,以及在console进行log确认


<<:  有关Wscript.exe *.vbs 的中文字编码( utf-8)问题

>>:  CodeWars : 新手村练等纪录03- Stop gninnipS My sdroW!

Dungeon Mizarka 018

MiniMap制作Part2 找到了tile生成时位置错乱的原因,还是因为进位造成的问题。A★内部是...

Node.js安装

昨天介绍了Node.js,今天我们就要来实际安装Node.js啦 首先当然是到官网去下载Node.j...

Day 05 - 了解FOREIGN KEY 外键限制!

上一篇在创造新的资料库时,有提到PRIMARY KEY也就是主键限制!那麽此篇来继续介绍FOREIG...

并行程序的潜在问题 (一)

建立 Thread 的成本远低於 Process 的成本,执行 Context switch 时的效...

Day24. 发动魔法卡,融合 - Composite (中)

昨天了解了 Composite 是什麽後,一如我们本来的安排,今天要来介绍的是 Composites...