72. 显示确认订单信息页
显示“确认订单信息”页面中,需要显示2种数据:当前用户的所有收货地址列表;用户在前序页面中选择的购物车中的商品。
首先,完成“显示当前用户的所有收货地址的列表”,在此前开发“收货地址”相关功能时,已经可以通过/addresses/这个URL获取收货地址列表!则直接在orderConfirm.html中通过$.ajax()获取数据并显示即可!
接下来,应该“显示用户在前序页面中选择的购物车中的商品的列表”,对应的查询功能的SQL语句大致是:- select * from t_cart left join t_product on t_cart.pid=t_product.id where cid in (?,?,?)
复制代码 所以,需要在CartMapper.java接口中添加:- List<CartVO> findByCids(Integer[] cids);
复制代码 在CartMapper.xml中配置以上笼统方法的映射:- <selectid="findByCids"resultType="xx.xx.xx.CartVO">
- SELECT
- cid, uid,
- pid, t_cart.num,
- t_cart.price, t_product.price AS realPrice,
- title, image
- FROM
- t_cart
- LEFT JOIN
- t_product
- ON
- t_cart.pid=t_product.id
- WHERE
- cid IN
- <foreachcollection="array"item="cid"seperator=","open="("close=")">
- #{cid}
- </foreach>
- ORDER BY
- t_cart.created_time DESC
- </select>
复制代码 完成耐久层后,接下来,需要在ICartService业务层接口中添加笼统方法,以对外提供数据访问功能:- List<CartVO> getByCids(Integer[] cids, Integer uid);
复制代码 然后,在实现类中,先私有化编写耐久层的方法:- private List<CartVO> findByCids(Integer[] cids) {
- return cartMapper.findByCids(cids);
- }
复制代码 并重写接口中的笼统方法:- public List<CartVO>getByCids(Integer[] cids, Integer uid){if(cids == null){returnnewArrayList<>();}
- List<CartVO> result =findByCids(cids);
- Iterator<CartVO> it = result.iterator();while(it.hasNext()){
- CartVO cartVO = it.next();if(cartVO.getUid()!= uid){
- it.remove();}}return result;}
- 测试完成后,需要在控制器层提供接口:
- @GetMapping("get_by_cids")public JsonResult<List<CartVO>>getByCids(
- Integer[] cids, HttpSession session){// 从session中获取uid
- Integer uid =getUidFromSession(session);// 调用业务层对象的方法执行任务
- List<CartVO> data = cartService.getByCids(cids, uid);// 响应胜利returnnewJsonResult<>(SUCCESS, data);}
复制代码 然后,翻开阅读器,可以通过http://localhost:8080/carts/get_by_cids?cids=14&cids=15&cids=16停止单元测试。
测试完成后,需要在orderConfirm.html中发出对这个URL的恳求,以获取数据,并显示。
73. 创建订单-创建数据表
创建“订单表”:- CREATE TABLE t_order (
- oid INT AUTO_INCREMENT COMMENT '订单id',
- uid INT COMMENT '用户id',
- recv_name VARCHAR(50) COMMENT '收货人receiver姓名',
- recv_phone VARCHAR(20) COMMENT '收货人电话',
- recv_province VARCHAR(50) COMMENT '收货地址所在省',
- recv_city VARCHAR(50) COMMENT '收货地址所在市',
- recv_area VARCHAR(50) COMMENT '收货地址所在区',
- recv_address VARCHAR(100) COMMENT '详细收货地址',
- total_price BIGINT COMMENT '总价',
- status INT COMMENT '状态:0-未支付,1-已支付,2-已取消',
- order_time DATETIME COMMENT '下单时间',
- pay_time DATETIME COMMENT '支付时间',
- created_user VARCHAR(50) COMMENT '创建人',
- created_time DATETIME COMMENT '创建时间',
- modified_user VARCHAR(50) COMMENT '最后修改人',
- modified_time DATETIME COMMENT '最后修改时间',
- PRIMARY KEY (oid)
- ) DEFAULT CHARSET=UTF8;
复制代码 创建“订单商品表”:- CREATE TABLE t_order_item (
- id INT AUTO_INCREMENT COMMENT 'id',
- oid INT COMMENT '归属的订单id',
- pid INT COMMENT '商品id',
- title VARCHAR(100) COMMENT '商品标题',
- image VARCHAR(500) COMMENT '商品图片',
- price BIGINT COMMENT '商品单价',
- num INT COMMENT '购置数量',
- created_user VARCHAR(50) COMMENT '创建人',
- created_time DATETIME COMMENT '创建时间',
- modified_user VARCHAR(50) COMMENT '最后修改人',
- modified_time DATETIME COMMENT '最后修改时间',
- PRIMARY KEY (id)
- ) DEFAULT CHARSET=UTF8;
复制代码 73. 创建订单-创建实体类
订单实体类:- /**
- * 订单数据的实体类
- */publicclassOrderextendsBaseEntity{privatestaticfinallong serialVersionUID =-3216224344757796927L;private Integer oid;private Integer uid;private String recvName;private String recvPhone;private String recvProvince;private String recvCity;private String recvArea;private String recvAddress;private Long totalPrice;private Integer status;private Date orderTime;private Date payTime;// ...}
- 订单商品实体类:
- /**
- * 订单商品数据的实体类
- */publicclassOrderItemextendsBaseEntity{privatestaticfinallong serialVersionUID =-8879247924788259070L;private Integer id;private Integer oid;private Integer pid;private String title;private String image;private Long price;private Integer num;// ...}
复制代码 74. 创建订单-耐久层
接口与笼统方法:- /**
- * 处置订单和订单商品数据的耐久层接口
- */
- public interface OrderMapper {
- /**
- * 插入订单数据
- * @param order 订单数据
- * @return 受影响的行数
- */
- Integer insertOrder(Order order);
-
- /**
- * 插入订单商品数据
- * @param orderItem 订单商品数据
- * @return 受影响的行数
- */
- Integer insertOrderItem(OrderItem orderItem);
-
- }
复制代码 映射:- <mappernamespace="cn.tedu.store.mapper.OrderMapper"><!-- 插入订单数据 --><!-- Integer insertOrder(Order order) --><insertid="insertOrder"useGeneratedKeys="true"keyProperty="oid">
- INSERT INTO t_order (
- uid, recv_name,
- recv_phone, recv_province,
- recv_city, recv_area,
- recv_address, total_price,
- status, order_time,
- pay_time,
- created_user, created_time,
- modified_user, modified_time
- ) VALUES (
- #{uid}, #{recvName},
- #{recvPhone}, #{recvProvince},
- #{recvCity}, #{recvArea},
- #{recvAddress}, #{totalPrice},
- #{status}, #{orderTime},
- #{payTime},
- #{createdUser}, #{createdTime},
- #{modifiedUser}, #{modifiedTime}
- )
- </insert><!-- 插入订单商品数据 --><!-- Integer insertOrderItem(OrderItem orderItem) --><insertid="insertOrderItem"useGeneratedKeys="true"keyProperty="id">
- INSERT INTO t_order_item (
- oid, pid,
- title, image,
- price, num,
- created_user, created_time,
- modified_user, modified_time
- ) VALUES (
- #{oid}, #{pid},
- #{title}, #{image},
- #{price}, #{num},
- #{createdUser}, #{createdTime},
- #{modifiedUser}, #{modifiedTime}
- )
- </insert></mapper>
复制代码 测试:- @RunWith(SpringRunner.class)@SpringBootTestpublicclassOrderMapperTests{@Autowired
- OrderMapper mapper;@TestpublicvoidinsertOrder(){
- Order order =newOrder();
- order.setUid(1);
- order.setRecvName("小李同学");
- order.setTotalPrice(10086L);
- Integer rows = mapper.insertOrder(order);
- System.err.println("rows="+ rows);}@TestpublicvoidinsertOrderItem(){
- OrderItem orderItem =newOrderItem();
- orderItem.setOid(1);
- orderItem.setTitle("某手机");
- orderItem.setPrice(3000L);
- orderItem.setNum(3);
- Integer rows = mapper.insertOrderItem(orderItem);
- System.err.println("rows="+ rows);}}
复制代码 75. 创建订单-业务层-根底功能
后续将需要根据收货地址的id,查询收货地址详情,需要在IAddressService中添加:- Address getByAid(Integer aid);
复制代码 在收货地址的业务层实现类AddressServiceImpl中,重写以上方法,调用已经存在的findByAid()私有方法即可实现。
创建cn.tedu.store.service.IOrderService业务层接口,在接口中添加笼统方法:- Order create(Integer aid, Integer[] cids, Integer uid, String username);
复制代码 创建cn.tedu.store.service.impl.OrderServiceImpl业务层实现类,实现以上接口,添加@Service注解,在类中添加@Autowired private OrderMapper orderMapper;耐久层对象,另外,还需要添加@Autowired private IAddressService addressService;收货地址的业务对象和@Autowired private ICartService cartService;购物车的业务对象。
在实现类中添加2个私有方法,对应耐久层的2个方法:- /**
- * 插入订单数据
- * @param order 订单数据
- * @throws InsertException 插入订单数据时呈现未知错误
- */privatevoidinsertOrder(Order order){
- Integer rows = orderMapper.insertOrder(order);if(rows !=1){thrownewInsertException("创建订单失败!插入订单数据时呈现未知错误!");}}/**
- * 插入订单商品数据
- * @param orderItem 订单商品数据
- * @throws InsertException 插入订单商品数据时呈现未知错误
- */privatevoidinsertOrderItem(OrderItem orderItem){
- Integer rows = orderMapper.insertOrderItem(orderItem);if(rows !=1){thrownewInsertException("创建订单失败!插入订单商品数据时呈现未知错误!");}}
复制代码 然后,重写接口中的笼统方法:- @Transactional
- public Order create(Integer aid, Integer[] cids, Integer uid, String username) {
- // 创建当前时间对象
- // 根据参数cids,通过cartService的getByCids()查询购物车数据,得到List<CartVO>类型的对象
- // 遍历以上购物车数据集合对象以计算总价
- // 创建Order对象
- // 补Order对象属性:uid > 参数uid
- // 根据参数aid,通过addressService的getByAid()方法查询收货地址详情
- // 补Order对象属性:recv_*
- // 补Order对象属性:total_price > 以上遍历时的计算结果
- // 补Order对象属性:status > 0
- // 补Order对象属性:order_time > 当前时间
- // 补Order对象属性:pay_time > null
- // 补Order对象属性:日志 > 参数username,当前时间
- // 插入订单数据:insertOrder(order)
- // 遍历购物车数据集合对象
- // -- 创建OrderItem对象
- // -- 补OrderItem对象属性:oid > order.getOid();
- // -- 补OrderItem对象属性:pid, title, image, price, num > 遍历对象中的pid, title, iamge ,realPrice, num
- // -- 补OrderItem对象属性:日志 > 参数username,当前时间
- // -- 插入订单商品数据:insertOrderItem(orderItem)
- // 未完,待续
- }
复制代码 实现:- @Override@Transactionalpublic Order create(Integer aid, Integer[] cids, Integer uid, String username){// 创建当前时间对象
- Date now =newDate();// 根据参数cids,通过cartService的getByCids()查询购物车数据,得到List<CartVO>类型的对象
- List<CartVO> carts = cartService.getByCids(cids, uid);// 遍历以上购物车数据集合对象以计算总价
- Long totalPrice =0L;for(CartVO cart : carts){
- totalPrice += cart.getRealPrice()* cart.getNum();}// 创建Order对象
- Order order =newOrder();// 补Order对象属性:uid > 参数uid
- order.setUid(uid);// 根据参数aid,通过addressService的getByAid()方法查询收货地址详情
- Address address = addressService.getByAid(aid);// 补Order对象属性:recv_*
- order.setRecvName(address.getName());
- order.setRecvPhone(address.getPhone());
- order.setRecvProvince(address.getProvinceName());
- order.setRecvCity(address.getCityName());
- order.setRecvArea(address.getAreaName());
- order.setRecvAddress(address.getAddress());// 补Order对象属性:total_price > 以上遍历时的计算结果
- order.setTotalPrice(totalPrice);// 补Order对象属性:status > 0
- order.setStatus(0);// 补Order对象属性:order_time > 当前时间// 补Order对象属性:pay_time > null
- order.setOrderTime(now);// 补Order对象属性:日志 > 参数username,当前时间
- order.setCreatedUser(username);
- order.setCreatedTime(now);
- order.setModifiedUser(username);
- order.setModifiedTime(now);// 插入订单数据:insertOrder(order)insertOrder(order);// 遍历购物车数据集合对象for(CartVO cart : carts){// 创建OrderItem对象
- OrderItem item =newOrderItem();// 补OrderItem对象属性:oid > order.getOid();
- item.setOid(order.getOid());// 补OrderItem对象属性:pid, title, image, price, num > 遍历对象中的pid, title, iamge ,realPrice, num
- item.setPid(cart.getPid());
- item.setTitle(cart.getTitle());
- item.setImage(cart.getImage());
- item.setPrice(cart.getRealPrice());
- item.setNum(cart.getNum());// 补OrderItem对象属性:日志 > 参数username,当前时间
- item.setCreatedUser(username);
- item.setCreatedTime(now);
- item.setModifiedUser(username);
- item.setModifiedTime(now);// 插入订单商品数据:insertOrderItem(orderItem)insertOrderItem(item);}// 未完,待续// 返回胜利创建的订单对象return order;}
- 测试:
- @RunWith(SpringRunner.class)@SpringBootTestpublicclassOrderServiceTests{@Autowired
- IOrderService service;@Testpublicvoidcreate(){
- Integer aid =25;
- Integer[] cids ={13,14,15,16,17,18,19,20};
- Integer uid =7;
- String username ="购物狂";
- Order result = service.create(aid, cids, uid, username);
- System.err.println(result);}}
复制代码 关于数据检查
以“用户注册”为例,用户可能需要向效劳器提交例如“用户名”等相关数据,但是,每种数据都应该有对应的数据格式要求,所以,在用户提交时,需要对数据的格式停止检查。
通常,在客户端就应该对数据的格式停止检查,例如在HTML中,可以使用Javascript程序对即将提交的数据停止检查,假设数据格式非法,则不会提交表单数据到效劳器端。
但是,即便客户端检查了数据,在效劳器端收到数据的第一时间,也就是在效劳器端的控制器中,仍需要对数据停止同样规范的检查!因为客户端可能是不可靠的,存在被窜改的可能!
虽然效劳器端会再次检查,但是,客户端的检查也是非常有必要的!因为客户端的检查可以拦截绝大部分数据格式有误的恳求,从而减轻效劳器端的压力。
另外,在有些应用中,在业务层收到数据的第一时间,也会检查数据!因为有些数据处置并不是由客户端提交并控制器已经检查过相关数据的!例如某些方案任务等。
所以:假设项目中始终是“客户端 --> 控制器层 --> 业务层”的处置流程,则只需要在客户端和效劳器端的控制器层各执行一次相同规范的格式检查即可,假设项目中还包含某些方案任务等不是由客户端发起恳求的数据处置流程,则相关的业务层也需要对数据停止检查!
76. 创建订单-业务层-肃清购物车对应的数据
当胜利的创建了订单后,还应该将购物车中匹配的数据停止删除!所以,在“创建订单”的业务中,还应该调用购物车的业务方法,执行删除,而购物车的业务层功能需要调用购物车的耐久层功能来完成这项操作,所以,应该先在购物车数据的耐久层CartMapper.java接口中开发出批量删除功能:- Integer deleteByCids(Integer[] cids);
复制代码 然后,在CartMapper.xml中配置映射:- <delete id="deleteByCids">
- DELETE FROM
- t_cart
- WHERE
- cid IN
- <foreach collection="array"
- item="cid" separator=","
- open="(" close=")">
- #{cid}
- </foreach>
- </delete>
复制代码 然后,在CartMapperTests中对这个功能停止测试:- @Test
- public void deleteByCids() {
- Integer[] cids = {17,18,19,20,21};
- Integer rows = mapper.deleteByCids(cids);
- System.err.println("rows=" + rows);
- }
复制代码 接下来,应该在ICartService业务层接口中添加笼统方法:- void delete(Integer[] cids, Integer uid);
复制代码 在CartServiceImpl业务层实现类中添加私有方法,以对应耐久层中新添加的批量删除功能:- /**
- * 根据若干个购物车数据id删除数据
- * @param cids 若干个购物车数据id
- * @throws DeleteException 删除数据异常
- */
- private void deleteByCids(Integer[] cids) {
- Integer rows = cartMapper.deleteByCids(cids);
- if (rows < 1) {
- throw new DeleteException(
- "肃清购物车数据失败!删除数据时发生未知错误!");
- }
- }
复制代码 然后,重写接口中的笼统方法:- public void delete(Integer[] cids) {
- // 判断即将删除的数据是否存在,及数据归属是否正确
- // 可以调用自身的:List<CartVO> getByCids(Integer[] cids, Integer uid),得到cid有效,且归属正确的购物车数据
- // 基于以上得到的List<CartVO>得到允许执行删除的cid的数组
- // 执行删除
- deleteByCids(cids);
- }
复制代码 实现为:- @Overridepublicvoiddelete(Integer[] cids, Integer uid){// 判断即将删除的数据是否存在,及数据归属是否正确// 可以调用自身的:List<CartVO> getByCids(Integer[] cids, Integer uid),得到cid有效,且归属正确的购物车数据
- List<CartVO> carts =getByCids(cids, uid);// 判断以上查询结果的长度是否有效if(carts.size()==0){thrownewCartNotFoundException("删除购物车数据失败!尝试访问的数据不存在!");}// 基于以上得到的List<CartVO>得到允许执行删除的cid的数组
- Integer[] ids =newInteger[carts.size()];for(int i =0; i < carts.size(); i++){
- ids[i]= carts.get(i).getCid();}// 执行删除deleteByCids(ids);}
- 完成后,执行单元测试:
- @TestpublicvoiddeleteByCids(){try{
- Integer[] cids ={13,15,23,25,27,29,31};
- Integer uid =1;
- service.delete(cids, uid);
- System.err.println("OK.");}catch(ServiceException e){
- System.err.println(e.getClass().getName());
- System.err.println(e.getMessage());}}
复制代码 以上功能完成后,就可以在创建订单的OrderServiceImpl的create()方法中补充删除的步骤:- // 删除购物车中对应的数据:cartService.deleteBy...
- cartService.delete(cids, uid);
复制代码 以上代码可以补充在插入订单数据、插入订单商品数据之前或之后,务必保证在第1次获取购物车数据之后再执行!
77. 创建订单-业务层-销库存
在创建订单时,应该在商品表t_product中减少对应商品的库存量,首先,就需要开发“减少库存”的功能:- update t_product set num=? where pid=?
复制代码 在执行减少库存之前,还应该查询商品数据是否存在、库存量是否充足,这些都可以先通过查询功能来实现,该查询功能已经存在,无需再次开发,所以,在处置商品数据的耐久层接口ProductMapper.java中需要添加1个笼统方法:- Integer updateNum(
- @Param("pid") Integer pid,
- @Param("num") Integer num);
复制代码 然后,在ProductMapper.xml中配置以上方法的映射:- <update id="updateNum">
- UPDATE
- t_product
- SET
- num=#{num}
- WHERE
- id=#{pid}
- </update>
复制代码 接下来处置商品数据的业务层,先创建必要的异常类:- ProductNotFoundException
- ProductOutOfStockException
复制代码 在IProductService接口中添加笼统方法:- // amount:减少的数量
- void reduceNum(Integer pid, Integer amount);
复制代码 然后,在处置商品数量的业务层实现中,先通过私有方法调用耐久层的更新商品数量的功能:- /**
- * 更新商品的库存
- * @param pid 商品的id
- * @param num 新的库存量
- * @throws UpdateException 更新商品数量失败
- */
- private void updateNum(Integer pid, Integer num) {
- Integer rows = productMapper.updateNum(pid, num);
- if (rows != 1) {
- throw new UpdateException(
- "更新商品数量失败!更新数据时呈现未知错误!");
- }
- }
复制代码 再实现接口中的笼统方法:- public void reduceNum(Integer pid, Integer amount) {
- // 通过参数pid查询商品数据
- // 判断查询结果是否为null:ProductNotFoundException
- // 判断查询结果中的num(当前库存)是否小于参数amount(将要购置或减少的库存量):ProductOutOfStockException
- // 执行减少库存
- }
复制代码 代码实现:- @OverridepublicvoidreduceNum(Integer pid, Integer amount){// 通过参数pid查询商品数据
- Product result =findById(pid);// 判断查询结果是否为null:ProductNotFoundExceptionif(result == null){thrownewProductNotFoundException("更新商品库存失败!尝试访问的商品数量不存在!");}// 暂不考虑商品下架的问题// 判断查询结果中的num(当前库存)是否小于参数amount(将要购置或减少的库存量):ProductOutOfStockExceptionif(result.getNum()< amount){thrownewProductOutOfStockException("更新商品库存失败!当前商品库存已经缺乏!");}// 执行减少库存updateNum(pid, result.getNum()- amount);}
- 完成后,执行测试:
- @TestpublicvoidreduceNum(){try{
- Integer pid =10000022;
- Integer amount =80;
- service.reduceNum(pid, amount);
- System.err.println("OK");}catch(ServiceException e){
- System.err.println(e.getClass().getName());
- System.err.println(e.getMessage());}}
复制代码 最后,在OrderServiceImpl中,添加@Autowired private IProductService productService;,并在create()方法中,在循环插入订单商品数据的同时,减少对应的商品的库存:- // 销库存
- productService.reduceNum(cart.getPid(), cart.getNum());
复制代码 --------------------------------
- public class xxThread extends Thread {
- public void run() {
- Thread.sleep(15 * 60 * 1000);
- // 检查订单状态是否为0
- // -- 修改订单状态
- // -- 回库存
- }
- }
- ### 处置超时未支付
复制代码 当订单生成后,用户必需在指定时间内完成支付,假设在指定时间内未支付(到达约定时间后订单状态status仍然为0,0-未支付,1-已支付,2-已取消,3-关闭),则应该关闭订单,关闭时,需要将订单状态停止修改,还需要归还该订单对应的库存。
更改订单状态需要执行的SQL语句大致是:- update t_order set status=3 where oid=?
复制代码 归还库存时,可以先根据购置的商品的pid查询商品数据(该功能已完成)获取商品的当前数据,再结合订单中估计购置的数据num得到最终归还后的数量,并更新商品表中该商品的数量(该功能已完成)。
所以,在耐久层的处置,只需要在OrderMapper.java中添加:- Integer updateStatus(Integer oid, Integer status, String username, Date modifiedTime);
复制代码 然后,在OrderMapper.xml中配置映射:- <!-- 修改订单状态 --><!-- Integer updateStatus(
- @Param("oid") Integer oid,
- @Param("status") Integer status,
- @Param("username") String username,
- @Param("modifiedTime") Date modifiedTime) --><updateid="updateStatus">
- UPDATE
- t_order
- SET
- status=#{status},
- modified_user=#{username},
- modified_time=#{modifiedTime}
- WHERE
- oid=#{oid}
- </update>
复制代码 再在OrderMapperTests中编写并执行单元测试:- @TestpublicvoidupdateStatus(){
- Integer oid =6;
- Integer status =100;
- String username ="系统";
- Date modifiedTime =newDate();
- Integer rows = mapper.updateStatus(oid, status, username, modifiedTime);
- System.err.println("rows="+ rows);}
复制代码 然后,还需要在耐久层添加“根据订单id查询订单状态”的功能:- Order findByOid(Integer oid);
复制代码 并配置该方法的映射:- <resultMapid="OrderEntityMap"type="cn.tedu.store.entity.Order"><idcolumn="oid"property="oid"/><resultcolumn="uid"property="uid"/><resultcolumn="recv_name"property="recvName"/><resultcolumn="recv_phone"property="recvPhone"/><resultcolumn="recv_province"property="recvProvince"/><resultcolumn="recv_city"property="recvCity"/><resultcolumn="recv_area"property="recvArea"/><resultcolumn="recv_address"property="recvAddress"/><resultcolumn="total_price"property="totalPrice"/><resultcolumn="status"property="status"/><resultcolumn="order_time"property="orderTime"/><resultcolumn="pay_time"property="payTime"/><resultcolumn="created_user"property="createdUser"/><resultcolumn="created_time"property="createdTime"/><resultcolumn="modified_user"property="modifiedUser"/><resultcolumn="modified_time"property="modifiedTime"/></resultMap><!-- 根据订单id查询订单详情 --><!-- Order findByOid(Integer oid) --><selectid="findByOid"resultMap="OrderEntityMap">
- SELECT
- *
- FROM
- t_order
- WHERE
- oid=#{oid}
- </select>
复制代码 执行单元测试:- @Test
- public void findByOid() {
- Integer oid = 6;
- Order result = mapper.findByOid(oid);
- System.err.println(result);
- }
复制代码 与“归还库存”相关的数据操作已完成,在耐久层并不需要添加新的数据处置功能。
接下来,还应该在业务层中提供“修改订单状态”、“增加商品库存”这2项功能,这2项功能合并起来,就是“归还库存”功能。
关于“修改订单状态”功能,应该在IOrderService中添加:- void changeStatus(Integer oid, Integer status, String username);
复制代码 在OrderServiceImpl中先添加私有方法:- /**
- * 修改订单状态
- * @param oid 订单id
- * @param status 状态值,0-未支付,1-已支付,2-已取消,3-关闭
- * @param username 修改执行人
- * @param modifiedTime 修改时间
- * @throws UpdateException 更新数据异常
- */privatevoidupdateStatus(Integer oid, Integer status,
- String username, Date modifiedTime)throws UpdateException {
- Integer rows = orderMapper.updateStatus(oid, status, username, modifiedTime);if(rows !=1){thrownewUpdateException("修改订单状态失败!更新数据时呈现未知错误!");}}/**
- * 根据订单id查询订单详情
- * @param oid 订单id
- * @return 匹配的订单详情,假设没有匹配的数据,则返回null
- */private Order findByOid(Integer oid){return orderMapper.findByOid(oid);}
- 然后,重写接口中的笼统方法:
- publicvoidchangeStatus(Integer oid, Integer status, String username){// 根据参数oid查询订单状态// 判断查询结果是否不存在:OrderNotFoundException// 执行修改订单状态}
- 实现:
- @OverridepublicvoidchangeStatus(Integer oid, Integer status, String username){// 根据参数oid查询订单状态
- Order result =findByOid(oid);// 判断查询结果是否不存在:OrderNotFoundExceptionif(result == null){thrownewOrderNotFoundException("修改订单状态失败!尝试访问的数据不存在!");}// 执行修改订单状态updateStatus(oid, status, username,newDate());}
- 完成后,测试:
- @TestpublicvoidchangeStatus(){try{
- Integer oid =6;
- Integer status =500;
- String username ="系统管理员";
- service.changeStatus(oid, status, username);
- System.err.println("OK.");}catch(ServiceException e){
- System.err.println(e.getClass().getName());
- System.err.println(e.getMessage());}}
复制代码 另外,还需要添加“增加商品库存”功能,该功能所需要耐久层操作已经完全完成,需要在IProductService中添加该功能:- void addNum(Integer pid, Integer amount);
复制代码 详细实现方式可参考此前已经完成的reduceNum()功能,实现代码为:- @OverridepublicvoidaddNum(Integer pid, Integer amount){// 通过参数pid查询商品数据
- Product result =findById(pid);// 判断查询结果是否为null:ProductNotFoundExceptionif(result == null){thrownewProductNotFoundException("更新商品库存失败!尝试访问的商品数量不存在!");}// 暂不考虑商品下架的问题// 执行增加库存updateNum(pid, result.getNum()+ amount);}
- 测试:
- @TestpublicvoidaddNum(){try{
- Integer pid =10000022;
- Integer amount =80;
- service.addNum(pid, amount);
- System.err.println("OK");}catch(ServiceException e){
- System.err.println(e.getClass().getName());
- System.err.println(e.getMessage());}}
复制代码 最后,在订单的业务层接口和实现类中添加“关闭订单”的操作:- @Override@Transactionalpublicvoidclose(Integer oid, List<OrderItem> orderItems, String username){// 检查订单状态是否是“未支付”
- Order result =findByOid(oid);// 检查查询结果是否不存在if(result == null){thrownewOrderNotFoundException("关闭订单失败!尝试访问的数据不存在!");}// 检查订单当前状态if(result.getStatus()!= Status.UNPAID){return;}// 将订单状态修改为已关闭changeStatus(oid, Status.CLOSED, username);// 归还订单中所有商品的库存for(OrderItem orderItem : orderItems){
- productService.addNum(orderItem.getPid(), orderItem.getNum());}
- System.err.println("OrderService:订单已关闭!");}
- 并且在`create()`创建订单的最后,添加方案任务:
- // 开启倒计时任务(Timer/Thread),假设用户在规定时间内未支付,则关闭订单,并归还库存newThread(){publicvoidrun(){
- System.err.println("OrderService:方案15分钟后检查订单状态,准备关闭订单");try{
- Thread.sleep(15*60*1000);}catch(InterruptedException e){
- e.printStackTrace();}
- System.err.println("OrderService:准备关闭单……");close(order.getOid(), orderItems, username);}}.start();
复制代码 |