项目档案

完善的拍卖体系

这些笔记将引导您完成拍卖系统的设计和构建。

这个系统相当大,所以我不打算在这些注释中介绍系统构造的每一个细节。相反,我将对最重要的系统细节给出一个高层次的概述。

方案布局

完整的拍卖系统使用了大约27种不同的职业。由于涉及到大量的类,我使用了一个包系统来帮助组织一切:

使用JdbcTemplate

这个赢博体育程序中的赢博体育数据库交互都通过JdbcTemplate对象。JdbcTemplate类提供了几个方法来进行数据库交互。在下面的注释中,我将展示我如何使用以下每种方法的例子:

前三个方法都与SQL选择语句结合使用,而第四个方法用于赢博体育其他类型的语句,如插入、更新和删除。

这个例子我们在之前的课堂讲稿中已经见过了。这是UserDAO在数据库中存储新用户的方法的代码:

public String save(User User){//首先确保这不是一个重复字符串sql = "SELECT * FROM users WHERE name=?";RowMapper<User> RowMapper = new UserRowMapper();用户旧= null;try {old = jdbcTemplate。查询forobject (sql, rowMapper, user.getName());} catch(Exception ex) {} if(old != null)返回“Duplicate”;//让MySQL生成唯一的id字符串idSQL = "select uuid()";String key = null;尝试{key = jdbcTemplate。queryForObject (idSQL String.class);} catch(Exception ex) {key = "Error";} if(key.equals("Error"))返回key;String hash = passwordService.hashPassword(user.getPassword());字符串insertSQL = "插入到用户(userid,name,password)的值(?, ?, ?);jdbcTemplate.update (insertSQL键,user.getName(),散列);返回键;}

这个例子展示了queryForObject()方法的两个略有不同的赢博体育程序。第一个赢博体育程序更为典型:我们执行一个SQL选择语句,该语句旨在从数据库返回0或1个User对象。在queryForObject()的那个版本中,第二个参数是一个RowMapper,用于生成我们期望从查询中返回的对象类型。queryForObject()的第二个赢博体育程序处理旨在返回字符串的select语句。对于那个版本的queryForObject(),我们只需使用第二个参数来表明我们期望查询返回一个Java String对象。

这个例子还展示了update()方法的一个典型赢博体育。这里我们使用update()来执行SQL插入语句。

我们将要执行的一些查询将被设计为返回多个对象。对于这些类型的查询,我们使用query()方法。下面是一个典型的例子:下面的代码是AuctionDAO的一个方法,它获取赢博体育当前打开的拍卖的列表:

public List<Auction> findActive() {String sql = "select * from auctions where open <=curdate() and close >=curdate()";列表<拍卖>结果= null;RowMapper<Auction> RowMapper = new AuctionRowMapper();尝试{结果= jdbcTemplate。查询(sql, rowMapper);} catch(Exception ex) {results = new ArrayList<Auction>();}返回结果;}

最后,有一些情况需要从数据库中读取不需要使用RowMapper的内容列表。在这些情况下,我们使用queryForList()来代替query()。我们可以在代码中看到一个示例,用于读取特定用户发布的赢博体育拍卖。我定义的Auction类有一个成员变量,该变量存储拍卖的标签列表。有时,当我从数据库中读取一个Auction对象时,我也会到数据库中的tags表中获取与该拍卖相关的标签列表。由于标签是简单的字符串,我将使用queryForList()来读取它们。下面是AuctionDAO类的方法,它读取特定用户发布的赢博体育拍卖:

public List<Auction> findByUser(String userid) {String sql = "select * from auctions where seller=?";列表<拍卖>结果= null;RowMapper<Auction> RowMapper = new AuctionRowMapper();尝试{结果= jdbcTemplate。查询(sql, rowMapper, userid)} catch (Exception ex) {results = new ArrayList<Auction>();} for (Auction a: results){字符串tagSQL = “从拍卖的标签中选择标签”;List<String> tags = jdbcTemplate。queryForList(tagSQL, String.class, a.g etautionid ());a.setTags(标签);}返回结果;}

控制器方法

拍卖赢博体育程序的最终版本支持广泛的API请求。API中有23种不同的交互,分布在三个独立的控制器类中。在之前的课堂讲稿中,我们已经看到了POST和GET交互的几个基本示例。在这些笔记中,我将讨论一些我们以前没有见过的API方法的例子。

拍卖API中的许多方法都使用了路径参数。下面是一个典型的例子:为了获取由特定用户上传的赢博体育Auction对象的列表,我们使用HTTP组合

GET /用户/ < id > /拍卖

其中 替换为用户id。下面是UserController中响应这个请求的方法:

@GetMapping("/{id}/auctions") public ResponseEntity<List<Auction>> getAuctions(@PathVariable String id) {List<Auction> results = auctionDAO.findByUser(id);返回ResponseEntity.ok () .body(结果);}

这里有两点很重要。首先,在@GetMapping注释中,我们使用特殊的符号{id}来表示URL中存在路径参数。其次,我们向该方法添加一个带@PathVariable注释的参数,以接收该路径参数的值。

我很少使用的另一个特性是查询参数。正如我在API设计指南注释中所解释的,查询参数很少用作过滤机制的一部分。在拍卖API中有一个这样的例子,对AuctionsController的GET请求获取赢博体育带有特定标签的拍卖:

@GetMapping(params= {"tag"}) public ResponseEntity<List<Auction b> > findActiveAuctionsByTag(@RequestParam(value = "tag") String tag) {List<Auction> auctions = dao.findActiveByTag(tag);返回ResponseEntity.ok () .body(拍卖);}

查询参数作为参数传递给使用@RequestParam注释的方法。

DTO类

除了在拍卖中定义的核心类之外。在core包中,我还发现需要创建七个额外的类来满足API的特定需求。这些都是将被发布到服务器(通常用于实现远程过程调用)或作为对各种GET请求的响应从服务器返回的类。

这里有几个典型的例子。

当用户刚刚赢得拍卖时,第一个例子变得很重要。要获取描述他们赢得的拍卖的对象列表,用户将使用URL /users/ /offers执行GET操作。下面是UserController中响应该请求的方法:

@GetMapping("/{id}/offers") public ResponseEntity<List<Offer>> getOffers(@PathVariable String id) {List<Offer> results = dao.findOffers(id);返回ResponseEntity.ok () .body(结果);}

此请求返回的Offer对象具有以下结构:

public class Offer {private String purchaseid;private String auctionid;私人的;私人int金额;}

这些Offer对象中的数据是数据库中purchase_detail视图中数据的子集。下面是从数据库中提取数据的DAO方法的代码:

public List<Offer> findOffers(String userId) {String sql = “select * from purchase_detail where投标人=? ”和状态= Won_bid”;购买列表< >购买;RowMapper<Purchase> RowMapper = new PurchaseRowMapper();尝试{购买= jdbcTemplate。查询(sql、rowMapper userId);} catch(Exception ex) {Purchase = new ArrayList<Purchase>();} ArrayList<Offer> result = new ArrayList<Offer>();for(Purchase p: purchases) {Offer 0 = new Offer(p);result.add (o);}返回结果;}

注意,这里使用的select语句最初是从数据库中提取Purchase对象列表。进入Offer对象的字段是这些字段的子集。为了将Purchase对象转换为Offer对象,我在Offer类中使用了一个特殊的构造函数,它从Purchase生成Offer:

public Offer(Purchase base) {purchaseid = base. getpurchaseid ();auctionid = base.getAuction();bidid = base.getBidid();amount = base.getBid();}

我对Sale和Bill类也使用了同样的技术。这三个类都实现了Purchase类的片段,以便向客户端传递更集中的信息。

另一组DTO类用于实现远程过程调用的参数。您可以在PurchaseController类中找到这些远程过程调用的许多示例。该类包含一系列方法,用于实现购买过程中的关键步骤。

这里有一个典型的例子。当用户执行如上所示的GET请求以获取他们赢得的拍卖的报价列表时,他们将被期望接受或拒绝每个报价。他们将通过一个远程过程调用来实现这一点,该过程调用涉及将一个OfferResponse对象发送到URL /purchases/ OfferResponse。下面是OfferResponse类的结构:

公共类OfferResponse{私有字符串purchaseid;private String userid;私有布尔接受;Private int shippingid;}

下面是PurchaseController中实现这个远程过程调用的方法:

@PostMapping("/offerresponse") public ResponseEntity<String> offerresponse(@RequestBody offerresponse or) {if(dao.saveOfferResponse(or)) return ResponseEntity.status(HttpStatus.CREATED)。身体(“提供接受”);else {dao. concludeuctions ();返回ResponseEntity.status (HttpStatus.ACCEPTED)。身体(“取消”);}}

PurchaseDAO类中在这里进行处理的方法也很有趣。它包含处理用户接受报价和拒绝报价两种情况的逻辑。

公共布尔saveOfferResponse(OfferResponse或){if(or. getaccept()){字符串sql = "更新购买设置shippingid=?, status='Confirmed' where purchaseid=?";jdbcTemplate.update (sql, or.getShippingid (), or.getPurchaseid ());返回true;} else{字符串searchPurchase = "select * from purchaseid=?";RowMapper<Purchase> RowMapper = new PurchaseRowMapper();购买结果;try {result = jdbcTemplate。queryForObject (searchPurchase rowMapper or.getPurchaseid ());} catch(Exception ex) {result = null;} if(result != null) {String deletePurchaseSQL = "delete from purchaseid=?";jdbcTemplate.update (deletePurchaseSQL);String deleteBidSQL = “从biddid =?的出价中删除”;jdbcTemplate.update (deleteBidSQL result.getBidid ());}返回false;}}

拒绝一个提议需要更复杂的过程。在这种情况下,我们必须查找购买的详细信息,从购买表中删除该购买,并找到导致生成此购买的中标,并将其从投标表中删除。