store(商城项目)Springboot+springmvc+ajax+mybatis(15-1)

83. 减去订单中的商品的库存

首先,应该在“管理商品数据”的持久层,添加“修改商品库存”的功能,需要执行的SQL语句大致是:

UPDATE
    t_product
SET
    num=#{num},
    modified_user=#{modifiedUser},
    modified_time=#{modifiedTime}
WHERE
    id=#{id}

除此以外,还应该先获取原来的库存值,需要“根据商品id查询商品详情”的功能,该功能已经开发,则无需重复开发!

所以,在ProductMapper.java接口中,需要添加:

Integer updateNumById(
    @Param("id") Integer id, 
    @Param("num") Integer num, 
    @Param("modifiedUser") String modifiedUser, 
    @Param("modifiedTime") Date modifiedTime);

然后配置映射:

<!-- 修改商品的库存 -->
<!-- Integer updateNumById(
        @Param("id") Integer id, 
        @Param("num") Integer num, 
        @Param("modifiedUser") String modifiedUser, 
        @Param("modifiedTime") Date modifiedTime) -->
<update id="updateNumById">
    UPDATE
        t_product
    SET
        num=#{num},
        modified_user=#{modifiedUser},
        modified_time=#{modifiedTime}
    WHERE
        id=#{id}
</update>

测试:

@Test
public void updateNumById() {
    Integer id = 10000001;
    Integer num = 50;
    String modifiedUser = "ADMIN";
    Date modifiedTime = new Date();
    Integer rows = mapper.updateNumById(id, num, modifiedUser, modifiedTime);
    System.err.println("rows=" + rows);
}

接下来,应该在“处理商品数据的业务层”实现“减少某商品的库存”的功能,则在IProductService接口中添加抽象方法:

void reduceNum(Integer id, Integer amount, String username);

然后,在ProductServiceImpl实现类,先添加私有的与持久层对应的updateNumById()方法:

/**
 * 修改商品的库存
 * @param id 商品id
 * @param num 新的库存值
 * @param modifiedUser 修改执行人
 * @param modifiedTime 修改时间
 * @return 受影响的行数
 */
private void updateNumById(Integer id, Integer num, 
        String modifiedUser, Date modifiedTime) {
    Integer rows = productMapper.updateNumById(id, num, modifiedUser, modifiedTime);
    if (rows != 1) {
        throw new UpdateException(
            "更新商品库存时出现未知错误,请联系系统管理员");
    }
}

然后,重写接口中的抽象方法:

public void reduceNum(Integer id, Integer amount, String username) {
    // 根据id查询商品数据
    // 判断查询结果是否为null
    // 是:抛出ProductNotFoundException

    // 取出查询结果中的原库存值
    // 结合参数amount计算出新的库存值
    // 判断新的库存值是否小于0
    // 是:抛出ProductOutOfStockException

    // 更新库存
}

代码:

@Override
public void reduceNum(Integer id, Integer amount, String username) {
    // 根据id查询商品数据
    Product result = findById(id);
    // 判断查询结果是否为null
    if (result == null) {
        // 是:抛出ProductNotFoundException
        throw new ProductNotFoundException(
                "尝试访问的商品数据不存在");
    }

    // 取出查询结果中的原库存值
    // 结合参数amount计算出新的库存值
    Integer newNum = result.getNum() - amount;
    // 判断新的库存值是否小于0
    if (newNum < 0) {
        // 是:抛出ProductOutOfStockException
        throw new ProductOutOfStockException(
            "商品库存不足");
    }

    // 更新库存
    updateNumById(id, newNum, username, new Date());
}

测试:

@Test
public void reduceNum() {
    try {
        Integer id = 10000001;
        Integer amount = 8;
        String username = "Hello";
        service.reduceNum(id, amount, username);
        System.err.println("OK.");
    } catch (ServiceException e) {
        System.err.println(e.getClass().getSimpleName());
        System.err.println(e.getMessage());
    }
}

84. 订单-创建订单-控制器

(a) 处理异常

需要处理ProductOutOfStockException

(b) 设计请求

请求路径:/orders/create
请求参数:Integer aid, Integer[] cids, HttpSession session
请求方式:POST
响应结果:JsonResult<Order>

(c) 处理请求

创建cn.tedu.store.controller.OrderController控制器类,继承自BaseController,添加@RestController@RequestMapping("orders")注解,声明@Autowired private IOrderService orderService;对象。

并在类中处理请求:

@RestController
@RequestMapping("orders")
public class OrderController extends BaseController {

    @Autowired
    private IOrderService orderService;

    @RequestMapping("create")
    public JsonResult<Order> create(Integer aid, Integer[] cids, HttpSession session) {
        Integer uid = getUidFromSession(session);
        String username = getUsernameFromSession(session);
        Order data = orderService.create(uid, username, aid, cids);
        return new JsonResult<>(OK, data);
    }

}

最后,在浏览器中测试:http://localhost:8080/orders/create?aid=42&cids=12&cids=13&cids=14&cids=15&cids=16

85. 订单-创建订单-前端页面

前端页面处理: 1. 第112行,<form>必须添加id; 2. 第117行,收货地址的<select>必须配置name="aid"; 3. 第169行,“在线支付”按钮必须添加id; 4. JS代码的showAddressList()中,生成列表项的<option>必须配置value="??",且值必须是aid; 5. JS代码的showCartList()中,生成页面代码中,每项数据必须添加<input type="hidden" name="cids" value="#{cid}" />,且后续占位符必须被替换为真实的cid。

86. Spring AOP

AOP:面向切面(Aspect)编程;

AOP并不是Spring的特有的功能,只不过是Spring很好的支持了AOP。

在一个项目中,关于功能的处理流程是:

注册: 前端页面 --> 控制器层 --> 业务层 --> 持久层

登录: 前端页面 --> 控制器层 --> 业务层 --> 持久层

改密: 前端页面 --> 控制器层 --> 业务层 --> 持久层

头像: 前端页面 --> 控制器层 --> 业务层 --> 持久层

如果,需要在每个功能的处理过程中,增加一些新的操作,例如计算业务层的执行耗时,或添加某些日志等!如果按照传统模式来实现,则应该将相关的代码封装在某个方法中,然后,在业务层的各方法中均调用这个封装的方法即可!

AOP在思想,是假想在整个处理流程中可以存在切面,切面中可以有方法,只要将切面加在处理流程中,就可以使得整个处理流程过程中执行切面中的方法!

在开发AOP之前,需要添加aspectj-toolsaspectj-weaver这2个依赖:

<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjtools</artifactId>
  <version>1.9.5</version>
</dependency>

<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.9.5</version>
</dependency>

使用AOP时,并不会对原有的处理业务的代码产生影响!首先,需要创建cn.tedu.store.aop.TimerAspect切面类,并在类之前添加@Aspect@Component注解:

@Aspect
@Component
public class TimerAspect {

}

然后,需要确定切面的作用方式,如果是添加在某个组件之前,后续需要使用@Before注解,如果是添加在某个组件之后,后续需要使用@After注解,如果是在某个组件之前和之后都需要执行,后续需要使用@Around注解!所以,如果需求是“统计业务层的执行耗时”,则应该在业务层执行之前记录时间,并在执行之后也记录时间,对比2个时间,就能得到执行耗时,所以,此功能需要使用@Around注解!

然后,还需要确定切面的连接点(作用于哪里),后续将通过表达式来设置!如果需要作用于业务层,可以将表达式设置为:

execution(* cn.tedu.store.service.impl.*.*(..))
// 无视返回值 impl包.所在类.所有方法(无视参数数量)

将以上注解中配置以上表达式,将注解添加在切面方法之前即可,关于切面方法的声明:

  1. 应该使用public权限;

  2. 如果使用@Around注解,需要使用Object作为返回值类型,表示业务层方法的返回值;

  3. 方法名可以自定义;

  4. 如果使用@Around注解,需要添加ProceedingJoinPoint作为参数,它是用于调用业务方法的!

所以,在切面类中添加切面方法:

@Aspect
@Component
public class TimerAspect {

    @Around("execution(* cn.tedu.store.service.impl.*.*(..))")
    public Object aaaaa(ProceedingJoinPoint pjp) {
        return null;
    }

}

最后,完成切面方法:

@Aspect
@Component
public class TimerAspect {

    @Around("execution(* cn.tedu.store.service.impl.*.*(..))")
    public Object aaaaa(ProceedingJoinPoint pjp) throws Throwable {
        // 记录起始时间
        long start = System.currentTimeMillis();

        // 执行业务层的方法,例如UserServiceImpl中的reg()方法或login()方法
        // 调用以下方法时,会出现异常,必须抛出,不可以try...catch
        Object obj = pjp.proceed();

        // 记录结束时间
        long end = System.currentTimeMillis();
        System.err.println("耗时:" + (end - start) + "ms.");

        // 必须返回以上proeceed()方法的返回值
        // 否则,相当于login()、reg()等业务方法都不会返回值
        return obj;
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

饭九钦vlog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值