计算机毕业设计|基于SpringBoot+MyBatis框架的电脑商城的设计与实现(订单和AOP)
该项目分析着重于设计和实现基于SpringBoot+MyBatis框架的电脑商城。首先,通过深入分析项目所需数据,包括用户、商品、商品类别、收藏、订单、购物车、收货地址,建立了数据模型。关于SpringBoot+MyBatis框架的电脑商城的设计与实现,我会按照系统概述与环境搭建、用户注册登录、用户资料修改、用户上传头像、-用户收货管理、商品、购物车、订单、AOP的顺序依次更新。本文内容主要是项目订单和AOP部分。
创建订单
1 订单-创建数据表
1.使用use命令先选中store数据库。
USE store;
2.在store数据库中创建t_order和t_order_item数据表。
CREATE TABLE t_order (
oid INT AUTO_INCREMENT COMMENT '订单id',
uid INT NOT NULL COMMENT '用户id',
recv_name VARCHAR(20) NOT NULL COMMENT '收货人姓名',
recv_phone VARCHAR(20) COMMENT '收货人电话',
recv_province VARCHAR(15) COMMENT '收货人所在省',
recv_city VARCHAR(15) COMMENT '收货人所在市',
recv_area VARCHAR(15) COMMENT '收货人所在区',
recv_address VARCHAR(50) COMMENT '收货详细地址',
total_price BIGINT COMMENT '总价',
status INT COMMENT '状态:0-未支付,1-已支付,2-已取消,3-已关闭,4-已完成',
order_time DATETIME COMMENT '下单时间',
pay_time DATETIME COMMENT '支付时间',
created_user VARCHAR(20) COMMENT '创建人',
created_time DATETIME COMMENT '创建时间',
modified_user VARCHAR(20) COMMENT '修改人',
modified_time DATETIME COMMENT '修改时间',
PRIMARY KEY (oid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE t_order_item (
id INT AUTO_INCREMENT COMMENT '订单中的商品记录的id',
oid INT NOT NULL COMMENT '所归属的订单的id',
pid INT NOT NULL COMMENT '商品的id',
title VARCHAR(100) NOT NULL COMMENT '商品标题',
image VARCHAR(500) COMMENT '商品图片',
price BIGINT COMMENT '商品价格',
num INT COMMENT '购买数量',
created_user VARCHAR(20) COMMENT '创建人',
created_time DATETIME COMMENT '创建时间',
modified_user VARCHAR(20) COMMENT '修改人',
modified_time DATETIME COMMENT '修改时间',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2 订单-创建实体类
1.在com.cy.store.entity包下创建Order实体类。
package com.cy.store.entity;
import java.io.Serializable;
import java.util.Date;
/** 订单数据的实体类 */
public class Order extends BaseEntity implements Serializable {
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;
// Generate: Getter and Setter、Generate hashCode() and equals()、toString()
}
2.在com.cy.store.entity包下创建OrderItem实体类。
package com.cy.store.entity;
import java.io.Serializable;
/** 订单中的商品数据 */
public class OrderItem extends BaseEntity implements Serializable {
private Integer id;
private Integer oid;
private Integer pid;
private String title;
private String image;
private Long price;
private Integer num;
// Generate: Getter and Setter、Generate hashCode() and equals()、toString()
}
3 订单-持久层
3.1 规划需要执行的SQL语句
1.插入订单数据的SQL语句大致是。
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 (
#对应字段的值列表
)
2.插入订单商品数据的SQL语句大致是。
INSERT INTO t_order_item (
oid,
pid,
title,
image,
price,
num,
created_user,
created_time,
modified_user,
modified_time
)
VALUES (
#对应字段的值列表
)
3.2 接口与抽象方法
在com.cy.store.mapper包下创建OrderMapper接口并在接口中添加抽象方法。
package com.cy.store.mapper;
import com.cy.store.entity.Order;
import com.cy.store.entity.OrderItem;
/** 处理订单及订单商品数据的持久层接口 */
public interface OrderMapper {
/**
* 插入订单数据
* @param order 订单数据
* @return 受影响的行数
*/
Integer insertOrder(Order order);
/**
* 插入订单商品数据
* @param orderItem 订单商品数据
* @return 受影响的行数
*/
Integer insertOrderItem(OrderItem orderItem);
}
3.3 配置SQL映射
1.在main\resources\mapper文件夹下创建OrderMapper.xml文件,并添加抽象方法的映射。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cy.store.mapper.OrderMapper">
<!-- 插入订单数据:Integer insertOrder(Order order) -->
<insert id="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) -->
<insert id="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>
2.在com.cy.store.mapper包下创建OrderMapperTests测试类,并添加测试方法。
package com.cy.store.mapper;
import com.cy.store.entity.Order;
import com.cy.store.entity.OrderItem;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class OrderMapperTests {
@Autowired
private OrderMapper orderMapper;
@Test
public void insertOrder() {
Order order = new Order();
order.setUid(31);
order.setRecvName("小王");
Integer rows = orderMapper.insertOrder(order);
System.out.println("rows=" + rows);
}
@Test
public void insertOrderItem() {
OrderItem orderItem = new OrderItem();
orderItem.setOid(1);
orderItem.setPid(2);
orderItem.setTitle("高档铅笔");
Integer rows = orderMapper.insertOrderItem(orderItem);
System.out.println("rows=" + rows);
}
}
4 订单-业务层
4.1 规划异常
说明:无异常。
4.2 接口与抽象方法
1.由于处理过程中还需要涉及收货地址数据的处理,所以需要先在IAddressService接口中添加getByAid()方法。
/**
* 根据收货地址数据的id,查询收货地址详情
* @param aid 收货地址id
* @param uid 归属的用户id
* @return 匹配的收货地址详情
*/
Address getByAid(Integer aid, Integer uid);
2.在AddressServiceImpl类中实现接口中的getByAid()抽象方法。
@Override
public Address getByAid(Integer aid, Integer uid) {
// 根据收货地址数据id,查询收货地址详情
Address address = addressMapper.findByAid(aid);
if (address == null) {
throw new AddressNotFoundException("尝试访问的收货地址数据不存在");
}
if (!address.getUid().equals(uid)) {
throw new AccessDeniedException("非法访问");
}
address.setProvinceCode(null);
address.setCityCode(null);
address.setAreaCode(null);
address.setCreatedUser(null);
address.setCreatedTime(null);
address.setModifiedUser(null);
address.setModifiedTime(null);
return address;
}
3.在com.cy.store.service包下创建IOrderService业务层接口并添加抽象方法。
package com.cy.store.service;
import com.cy.store.entity.Order;
/** 处理订单和订单数据的业务层接口 */
public interface IOrderService {
/**
* 创建订单
* @param aid 收货地址的id
* @param cids 即将购买的商品数据在购物车表中的id
* @param uid 当前登录的用户的id
* @param username 当前登录的用户名
* @return 成功创建的订单数据
*/
Order create(Integer aid, Integer[] cids, Integer uid, String username);
}
4.3 实现抽象方法
1.在com.cy.store.service.impl包下创建OrderServiceImpl业务层实现类并实现IOrderService接口;在类定义之前添加@Service注解,在类中添加OrderMapper订单持久层对象、IAddressService处理收货地址对象、ICartService购物车数据对象,并都添加@Autowired注解进行修饰。
package com.cy.store.service.impl;
import com.cy.store.entity.Address;
import com.cy.store.entity.Order;
import com.cy.store.entity.OrderItem;
import com.cy.store.mapper.OrderMapper;
import com.cy.store.service.IAddressService;
import com.cy.store.service.ICartService;
import com.cy.store.service.IOrderService;
import com.cy.store.service.ex.InsertException;
import com.cy.store.vo.CartVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
/** 处理订单和订单数据的业务层实现类 */
@Service
public class OrderServiceImpl implements IOrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private IAddressService addressService;
@Autowired
private ICartService cartService;
// ...
}
2.在OrderServiceImpl类中重写父接口中的create()抽象方法。
@Transactional
@Override
public Order create(Integer aid, Integer[] cids, Integer uid, String username) {
// 创建当前时间对象
// 根据cids查询所勾选的购物车列表中的数据
// 计算这些商品的总价
// 创建订单数据对象
// 补全数据:uid
// 查询收货地址数据
// 补全数据:收货地址相关的6项
// 补全数据:totalPrice
// 补全数据:status
// 补全数据:下单时间
// 补全数据:日志
// 插入订单数据
// 遍历carts,循环插入订单商品数据
// 创建订单商品数据
// 补全数据:oid(order.getOid())
// 补全数据:pid, title, image, price, num
// 补全数据:4项日志
// 插入订单商品数据
// 返回
}
3.OrderServiceImpl类中的create()方法具体逻辑代码实现见下。
@Transactional
@Override
public Order create(Integer aid, Integer[] cids, Integer uid, String username) {
// 创建当前时间对象
Date now = new Date();
// 根据cids查询所勾选的购物车列表中的数据
List<CartVO> carts = cartService.getVOByCids(uid, cids);
// 计算这些商品的总价
long totalPrice = 0;
for (CartVO cart : carts) {
totalPrice += cart.getRealPrice() * cart.getNum();
}
// 创建订单数据对象
Order order = new Order();
// 补全数据:uid
order.setUid(uid);
// 查询收货地址数据
Address address = addressService.getByAid(aid, uid);
// 补全数据:收货地址相关的6项
order.setRecvName(address.getName());
order.setRecvPhone(address.getPhone());
order.setRecvProvince(address.getProvinceName());
order.setRecvCity(address.getCityName());
order.setRecvArea(address.getAreaName());
order.setRecvAddress(address.getAddress());
// 补全数据:totalPrice
order.setTotalPrice(totalPrice);
// 补全数据:status
order.setStatus(0);
// 补全数据:下单时间
order.setOrderTime(now);
// 补全数据:日志
order.setCreatedUser(username);
order.setCreatedTime(now);
order.setModifiedUser(username);
order.setModifiedTime(now);
// 插入订单数据
Integer rows1 = orderMapper.insertOrder(order);
if (rows1 != 1) {
throw new InsertException("插入订单数据时出现未知错误,请联系系统管理员");
}
// 遍历carts,循环插入订单商品数据
for (CartVO cart : carts) {
// 创建订单商品数据
OrderItem item = new OrderItem();
// 补全数据:setOid(order.getOid())
item.setOid(order.getOid());
// 补全数据:pid, title, image, price, num
item.setPid(cart.getPid());
item.setTitle(cart.getTitle());
item.setImage(cart.getImage());
item.setPrice(cart.getRealPrice());
item.setNum(cart.getNum());
// 补全数据:4项日志
item.setCreatedUser(username);
item.setCreatedTime(now);
item.setModifiedUser(username);
item.setModifiedTime(now);
// 插入订单商品数据
Integer rows2 = orderMapper.insertOrderItem(item);
if (rows2 != 1) {
throw new InsertException("插入订单商品数据时出现未知错误,请联系系统管理员");
}
}
// 返回
return order;
}
4.在com.cy.store.service测试包下创建OrderServiceTests测试类,并添加create()方法进行功能测试。
package com.cy.store.service;
import com.cy.store.entity.Order;
import com.cy.store.service.ex.ServiceException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class OrderServiceTests {
@Autowired
private IOrderService orderService;
@Test
public void create() {
try {
Integer aid = 21;
Integer[] cids = {4, 5, 6,7};
Integer uid = 31;
String username = "订单管理员";
Order order = orderService.create(aid, cids, uid, username);
System.out.println(order);
} catch (ServiceException e) {
System.out.println(e.getClass().getSimpleName());
System.out.println(e.getMessage());
}
}
}
5 订单-控制器层
5.1 处理异常
说明:无异常。
5.2 设计请求
设计用户提交的请求,并设计响应的方式。
请求路径:/orders/create
请求参数:Integer aid, Integer[] cids, HttpSession session
请求类型:POST
响应结果:JsonResult<Order>
5.3 处理请求
1.在com.cy.store.controller包下创建OrderController类,并继承自BaseController类;并在类前添加@RequestMapping(“orders”)注解和@RestController注解;在类中声明IOrderService业务对象,然后添加@Autowired注解修饰;最后在类中添加处理请求的方法。
package com.cy.store.controller;
import com.cy.store.entity.Order;
import com.cy.store.service.IOrderService;
import com.cy.store.util.JsonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
@RestController
@RequestMapping("orders")
public class OrderController extends BaseController {
@Autowired
private IOrderService orderService;
@RequestMapping("create")
public JsonResult<Order> create(Integer aid, Integer[] cids, HttpSession session) {
// 从Session中取出uid和username
Integer uid = getUidFromSession(session);
String username = getUsernameFromSession(session);
// 调用业务对象执行业务
Order data = orderService.create(aid, cids, uid, username);
// 返回成功与数据
return new JsonResult<Order>(OK, data);
}
}
2.完成后启动项目,先登录再访问http://localhost:8080/orders/create?aid=21&cids=4&cids=5&cids=6&cids=7进行测试。
6 订单-前端页面
1.在orderConfirm.xml页面中的body标签内的script标签内添加“在线支付”按钮的点击时间。
$("#btn-create-order").click(function() {
$.ajax({
url: "/orders/create",
data: $("#form-create-order").serialize(),
type: "POST",
dataType: "JSON",
success: function(json) {
if (json.state == 200) {
alert("创建订单成功!");
console.log(json.data);
} else {
alert("创建订单失败!" + json.message);
}
},
error: function(xhr) {
alert("您的登录信息已经过期,请重新登录!HTTP响应码:" + xhr.status);
location.href = "login.html";
}
});
});
2.完成后启动项目,先登录再访问http://localhost:8080/web/cart.html页面,勾选购车中的商品,再点击“结算”按钮,最后在订单确认页中点击“在线支付”按钮进行功能的测试。
AOP
1 Spring AOP
AOP:面向切面(Aspect)编程。AOP并不是Spring框架的特性,只是Spring很好的支持了AOP。
如果需要在处理每个业务时,都执行特定的代码,则可以假设在整个数据处理流程中存在某个切面,切面中可以定义某些方法,当处理流程执行到切面时,就会自动执行切面中的方法。最终实现的效果就是:只需要定义好切面方法,配置好切面的位置(连接点),在不需要修改原有数据处理流程的代码的基础之上,就可以使得若干个流程都执行相同的代码。
2 切面方法
1.切面方法的访问权限是public。
2.切面方法的返回值类型可以是void或Object,如果使用的注解是@Around时,必须使用Object作为返回值类型,并返回连接点方法的返回值;如果使用的注解是@Before或@After等其他注解时,则自行决定。
3.切面方法的名称可以自定义。
4.切面方法的参数列表中可以添加ProceedingJoinPoint接口类型的对象,该对象表示连接点,也可以理解调用切面所在位置对应的方法的对象,如果使用的注解是@Around时,必须添加该参数,反之则不是必须添加。
3 统计业务方法执行时长
1.在使用Spring AOP编程时,需要先在pom.xml文件中添加两个关于AOP的依赖aspectjweaver和aspectjtools。
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
</dependency>
2.在com.cy.store.aop包下创建TimerAspect切面类,在类之前添加@Aspect和@Component注解修饰。
package com.cy.store.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class TimerAspect {
}
3.在类中添加切面方法around(ProceedingJoinPoint pjp)。
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// 记录起始时间
long start = System.currentTimeMillis();
// 执行连接点方法,即切面所在位置对应的方法。本项目中表示执行注册或执行登录等
Object result = pjp.proceed();
// 记录结束时间
long end = System.currentTimeMillis();
// 计算耗时
System.err.println("耗时:" + (end - start) + "ms.");
// 返回连接点方法的返回值
return result;
}
4.最后需要在方法之前添加@Around注解,以配置连接点,即哪些方法需要应用该切面。
@Around("execution(* com.cy.store.service.impl.*.*(..))")
5.启动项目,在前端浏览器访问任意一个功能模块进行功能的测试。
至此,基于SpringBoot+MyBatis框架的电脑商城的设计与实现所有部分已经全部更新完成