SpringBoot后端实现全接口实现商城管理系统
技术栈
后端使用的是SpringBoot+mybaris+maven+RabbitMQ(进行异步通知)+支付接口(微信和支付宝均采用的是Native支付)
- 支付文本转换为二维码 利用浏览进行转换 避免后端代码实现 减少服务器压力
因为整体的执行流程的需要,支付板块被单独分隔开,所以此处支付是独立系统,在下一篇文章进行更新说明。
本次的所有的后端业务接口均是单例测试Service层,所有的后端API接口是通过Postman进行测试的。
项目结构
对应文件结构
Vo文件 对应给前端返回格式
form文件 对应前端给后端传入参数的格式
exception文件 是对运行时的异常进行处理 返回提示消息给前端
enums文件 枚举字段类
UserLoginInterceptor 对应的拦截器
InterceptorConfig 对应的拦截器进行配置 主要就是对cooike中的sessioid进行见检查
数据库的表结构
环境配置使用的是
pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.dsq</groupId>
<artifactId>mall</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mall</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.13</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<!-- 重复生成代码时, xml 文件会覆盖-->
<configuration>
<overwrite>true</overwrite>
</configuration>
</plugin>
</plugins>
</build>
</project>
Lombok
- 该插件可以自动打入get和set方法
- 需要在setting中的plugin进行下载lombok 以及pom.xml中导入
配置文件
application.xml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: 用户名
password: 密码
url: jdbc:mysql://127.0.0.1:3306/mall?useUnicode=true&characterEncoding=gbk&useSSL=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Hongkong
redis:
host: 主机名
port: 6379
password: 密码
mybatis:
configuration:
map-underscore-to-camel-case: true
# 用mybtais进行打印对应的sql语句
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:mapper/*.xml
server:
servlet:
session:
timeout: 此处设置session存货时间默认是30min
**此处一定注意自己对资源文件路径的扫描 ,即mapper-locations **
- 同时注意自己的target文件中的clasess
Cookie跨域 不同的域名 保存的cookie是不同的
- Localhost
- 127.0.0.1
Ip和域名不一样 都算是跨域 对应存储的cookie是不相同的
本次的数据库mysql是8.0+ 所以在url需要加时区。
5.0+的mysql版本需要区别cj
com.mysql.cj.jdbc.Driver
此处采用的是Mybatis-generator 生成器
生成器执行逻辑
首先就是连接数据库 获取表结构 生成文件 由数据库的表生成java代码
此处的myabatis使用逆向工程 最后根据真实的代码需求 进行对应的mapper.xml中的东涛sql语句进行更新
对应的Terminal执行命令
mvn mybatis-generator:generate
对应的mybatis生成器
generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<!--配置生成器-->
<generatorConfiguration>
<!-- 用于加载配置项或者配置文件-->
<!-- 连接数据库所需要的驱动-->
<classPathEntry location="C:\Users\hp\Desktop\org\lib\mysql-connector-java-8.0.22.jar" />
<context id="DB2Tables" targetRuntime="MyBatis3">
<!-- 不再追加xml内容-->
<plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin" />
<!-- 不进行加载注释 注意这个commentGenerator是有加载顺序的-->
<commentGenerator>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/mall?useUnicode=true&characterEncoding=gbk&useSSL=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Hongkong"
userId="root"
password="123456">
</jdbcConnection>
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- java模型创建器 必须要的元素-->
<javaModelGenerator targetPackage="com.dsq.mall.pojo" targetProject="src/main/java">
<!-- 子包是否可用 根据数据库的scherma在生成一层package 将生成的类放在这个package下-->
<property name="enableSubPackages" value="true" />
<!-- 可以使用@Data <property name="trimStrings" value="true" />-->
</javaModelGenerator>
<sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER" targetPackage="com.dsp.mall.dao" targetProject="src/main/java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<table tableName="mall_shipping" domainObjectName="Shipping" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="false" enableUpdateByExample="false"/>
</context>
</generatorConfiguration>
解释
tableName 表示对应的数据库中的表名
domainObjectName 新文件生成的名字
之后的意思就是代码逻辑就是消除注释
Mybatis-PageHelper 进行分页的 使用的物理分页
使用调用核心代码
- PageHelper.startPage(pageNum, pageSize);
- PageInfo pageInfo = new PageInfo<>();
接下来就是三大层结构 dao层 service层 controller层
开发顺序: Dao->Service->Controller
对应的方法和实现类
整代码的逻辑核心层都在Service 此处侧重展示Service层
用户模块
HXR中可以进行区分 请求时浏览器直接渲染的 还是通过后端API接口调用的
在Response中可以看到 页面上的数据是否时从后端API得到的 后端返回的是json格式 前端拿到后 对这些数据 进行渲染
UserServiceImpl
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
@Override
public ResponseVo<User> register(User user) {
// 检验数据 username不能重复
int countByUsername = userMapper.countByUsername(user.getUsername());
if(countByUsername>0){
throw new RuntimeException("该username已经注册");
}
// email不能重复
int countByEmail = userMapper.countByEmail(user.getEmail());
if(countByEmail>0){
// throw new RuntimeException("该email已经注册");
return ResponseVo.error(ResponseEnum.ERROR);
}
user.setRole(RoleEnum.CUSTOMER.getCode());
// 密码MD5 Digest 摘要(Spring自带)
user.setPassword(DigestUtils.md5DigestAsHex(user.getPassword().getBytes(StandardCharsets.UTF_8)));
// 将数据写入数据库中
int resultcount = userMapper.insertSelective(user);
if(resultcount ==0){
return ResponseVo.error(ResponseEnum.ERROR);
}
return ResponseVo.success();
}
@Override
public ResponseVo<User> login(String username, String password) {
User user = userMapper.selectByUsername(username);
if(user== null){
// 用户不存在 (应该提示密码或者用户名错误 提高安全性)
return ResponseVo.error(ResponseEnum.USERNAME_OR_PASSWORD_ERROR);
}
if (!user.getPassword().equalsIgnoreCase(
DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8)))){
// 密码错误 (应该提示密码或者用户名错误 提高安全性)
return ResponseVo.error(ResponseEnum.USERNAME_OR_PASSWORD_ERROR);
}
user.setPassword("");
return ResponseVo.success(user);
}
}
商品列表的开发模块
ProductServiceImpl
@Service
@Slf4j
public class ProductServiceImpl implements IProductService {
@Autowired
private ICategoryService categoryService;
@Autowired
private ProductMapper productMapper;
@Override
public ResponseVo<PageInfo> list(Integer categoryId, Integer pageNum, Integer pageSize) {
Set<Integer> categoryIdSet = new HashSet<>();
if(categoryId != null){
categoryService.findSubCategoryId(categoryId,categoryIdSet);
categoryIdSet.add(categoryId);
}
PageHelper.startPage(pageNum,pageSize);
List<Product> productList = productMapper.selectByCategoryIdSet(categoryIdSet);
List<ProductVo> productVoList =productList.stream()
.map(e ->{
// 将属性进行拷贝 返回productVo 对象
ProductVo productVo = new ProductVo();
BeanUtils.copyProperties(e,productVo);
return productVo;
})
// 最后进行一个收集
.collect(Collectors.toList());
PageInfo pageInfo = new PageInfo<>(productList);
pageInfo.setList(productVoList);
return ResponseVo.success(pageInfo);
}
@Override
public ResponseVo<ProductDetailVo> detail(Integer productId) {
Product product = productMapper.selectByPrimaryKey(productId);
if(product.getStatus().equals(OFF_SALE) || product.getStatus().equals(DELETE) ){
return ResponseVo.error(ResponseEnum.PRODUCT_OFF_SALE_OR_DELETE);
}
// 对象类型的转换
ProductDetailVo productDetailVo = new ProductDetailVo();
BeanUtils.copyProperties(product,productDetailVo);
// 对应库存敏感信息 不进行真实的返回 防止爬虫机制
productDetailVo.setStock(product.getStock() > 100 ? 100: product.getStock());
return ResponseVo.success(productDetailVo);
}
}
其中
商品详情的接口开发
在做if语句进行判断的时候 只对确定性的条件进行判断
就会出现·多重选择
购物车模块的开发
ICartServiceImpl
@Service
public class ICartServiceImpl implements ICartService {
private final static String CART_REDIS_KEY_TEMPLATE = "cart_%d";
@Autowired
private ProductMapper productMapper;
@Autowired
private StringRedisTemplate redisTemplate;
private Gson gson = new Gson();
@Override
public ResponseVo<CartVo> add(Integer uid, CartAddForm form) {
Integer quantity = 1;
Product product = productMapper.selectByPrimaryKey(form.getProductId());
// 商品是否存在
if (product == null)
return ResponseVo.error(ResponseEnum.PRODUCT_NOT_EXIST);
// 商品库存是否充足
if (!product.getStatus().equals(ProductStatusEnum.ON_SALE))
return ResponseVo.error(ResponseEnum.PRODUCT_OFF_SALE_OR_DELETE);
// 商品是否是正常的在售的状态
if (product.getStock() <= 0)
return ResponseVo.error(ResponseEnum.PRODUCT_NOT_ERROR);
String rediskey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
// 写入到redis
// key是自己定义的
// 参数 第一个是key key此处不用Integer value
HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
String value = opsForHash.get(rediskey, String.valueOf(product.getId()));
Cart cart;
if (StringUtils.isEmpty(value)) {
// 没有该商品 新增
cart = new Cart(product.getId(), quantity, form.getSelected());
} else {
// 已经有了 数量+1
cart = gson.fromJson(value, Cart.class);
cart.setQuantity(cart.getQuantity() + quantity);
}
opsForHash.put(rediskey
, String.valueOf(product.getId())
// 字符进行序列化 将一个对象转换为一个字符
, gson.toJson(cart));
return list(uid);
}
@Override
public ResponseVo<CartVo> list(Integer uid) {
HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
String rediskey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
Map<String, String> entries = opsForHash.entries(rediskey);
boolean selectAll = true;
Integer carTotalQuantity = 0;
BigDecimal cartTotalPrice =BigDecimal.ZERO;
CartVo cartVo = new CartVo();
List<CartProductVo> cartProductVoList = new ArrayList<>();
for (Map.Entry<String, String> entry : entries.entrySet()) {
Integer productId = Integer.valueOf(entry.getKey());
Cart cart = gson.fromJson(entry.getValue(), Cart.class);
// TODO 需要优化 使用mysql里的in
Product product = productMapper.selectByPrimaryKey(productId);
if (product != null) {
CartProductVo cartProductVo = new CartProductVo(
productId,
cart.getQuantity(),
product.getName(),
product.getSubtitle(),
product.getMainImage(),
product.getPrice(),
product.getStatus(),
product.getPrice().multiply(BigDecimal.valueOf(cart.getQuantity())),
product.getStock(),
cart.getProductSelected()
);
cartProductVoList.add(cartProductVo);
if(! cart.getProductSelected()){
selectAll=false;
}
// 计算总价 只选中的
if(cart.getProductSelected())
cartTotalPrice = cartTotalPrice.add(cartProductVo.getProductTotalPrice());
}
carTotalQuantity += cart.getQuantity();
}
// 是否全部选择
cartVo.setSelectAll(selectAll);
cartVo.setCartTotalQuantity(carTotalQuantity);
cartVo.setCartTotalPrice(cartTotalPrice);
cartVo.setCartProductVo(cartProductVoList);
return ResponseVo.success(cartVo);
}
@Override
public ResponseVo<CartVo> update(Integer uid, Integer productId, CartUpdateForm form) {
HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
String rediskey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
String value = opsForHash.get(rediskey, String.valueOf(productId));
if (StringUtils.isEmpty(value)) {
// 没有该商品 购物车不存在该商品
return ResponseVo.error(ResponseEnum.CART_PRODUCT_NOT_EXIST);
}
// 已经有了 进行修改数量内容
Cart cart = gson.fromJson(value, Cart.class);
if(form.getQuantity() != null && form.getQuantity() >=0){
cart.setQuantity(form.getQuantity());
}
if(form.getSelected() != null ){
cart.setProductSelected(form.getSelected());
}
opsForHash.put(rediskey,String.valueOf(productId),gson.toJson(cart));
return list(uid);
}
@Override
public ResponseVo<CartVo> delete(Integer uid, Integer productId) {
HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
String rediskey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
String value = opsForHash.get(rediskey, String.valueOf(productId));
if (StringUtils.isEmpty(value)) {
// 没有该商品 购物车不存在该商品
return ResponseVo.error(ResponseEnum.CART_PRODUCT_NOT_EXIST);
}
opsForHash.delete(rediskey,String.valueOf(productId));
return list(uid);
}
@Override
public ResponseVo<CartVo> selectAll(Integer uid) {
HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
String rediskey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
for (Cart cart : listForCart(uid)) {
cart.setProductSelected(true);
opsForHash.put(rediskey,
String.valueOf(cart.getProductId()),
gson.toJson(cart)
);
}
return list(uid);
}
@Override
public ResponseVo<CartVo> unselectAll(Integer uid) {
HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
String rediskey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
for (Cart cart : listForCart(uid)) {
cart.setProductSelected(false);
opsForHash.put(rediskey,
String.valueOf(cart.getProductId()),
gson.toJson(cart)
);
}
return list(uid);
}
@Override
public ResponseVo<Integer> sum(Integer uid) {
Integer sum = listForCart(uid).stream()
.map(Cart::getQuantity)
.reduce(0, Integer::sum);
return ResponseVo.success(sum);
}
public List<Cart> listForCart(Integer uid){
HashOperations<String, String, String> opsForHash = redisTemplate.opsForHash();
String rediskey = String.format(CART_REDIS_KEY_TEMPLATE, uid);
Map<String, String> entries = opsForHash.entries(rediskey);
List<Cart> cartList = new ArrayList<>();
for (Map.Entry<String, String> entry : entries.entrySet()) {
cartList.add(gson.fromJson(entry.getValue(),Cart.class));
}
return cartList;
}
}
Redis(高性能)
应用场景 比如某篇文件的点赞和取消点赞
如果是大数据 会采用Elasticsearch/HBase(大数据)
MongDB(海量数据)
应用场景 做一个社交软件 需要存储海量的数据时候 可以使用这个
故此处购物车的缓存使用的是Redis
订单模块的开发
难点 数据多 流程多
订单:
不可变的数据 图片、收货信息
可变的的数据 支付状态
OrderServiceImpl
@Service
public class OrderServiceImpl implements IOrderService {
@Autowired
private ShippingMapper shippingMapper;
@Autowired
private ICartService cartService;
@Autowired
private ProductMapper productMapper;
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderItemMapper orderItemMapper;
@Override
// 对事务的控制
@Transactional
public ResponseVo<OrderVo> create(Integer uid, Integer shippingId) {
//收货地址校验(总之要查出来的)
Shipping shipping = shippingMapper.selectUidByAndShippingId(uid, shippingId);
if (shipping == null) {
return ResponseVo.error(ResponseEnum.SHIPPING_NOT_EXIST);
}
//获取购物车,校验(是否有商品、库存)
List<Cart> cartList = cartService.listForCart(uid).stream()
.filter(Cart::getProductSelected)
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(cartList)) {
return ResponseVo.error(ResponseEnum.CART_SELECTED_EMPTY);
}
//获取cartList里的productIds
Set<Integer> productIdSet = cartList.stream()
.map(Cart::getProductId)
.collect(Collectors.toSet());
List<Product> productList = productMapper.selectByProductIdSet(productIdSet);
Map<Integer, Product> map = productList.stream()
.collect(Collectors.toMap(Product::getId, product -> product));
List<OrderItem> orderItemList = new ArrayList<>();
Long orderNo = generateOrderNo();
for (Cart cart : cartList) {
//根据productId查数据库
Product product = map.get(cart.getProductId());
//是否有商品
if (product == null) {
return ResponseVo.error(ResponseEnum.PRODUCT_NOT_EXIST,
"商品不存在. productId = " + cart.getProductId());
}
//商品上下架状态
if (!ProductStatusEnum.ON_SALE.getCode().equals(product.getStatus())) {
return ResponseVo.error(ResponseEnum.PRODUCT_OFF_SALE_OR_DELETE,
"商品不是在售状态. " + product.getName());
}
//库存是否充足
if (product.getStock() < cart.getQuantity()) {
return ResponseVo.error(ResponseEnum.PRODUCT_NOT_ERROR,
"库存不正确. " + product.getName());
}
OrderItem orderItem = buildOrderItem(uid, orderNo, cart.getQuantity(), product);
orderItemList.add(orderItem);
//减库存
product.setStock(product.getStock() - cart.getQuantity());
int row = productMapper.updateByPrimaryKeySelective(product);
if (row <= 0) {
return ResponseVo.error(ResponseEnum.ERROR);
}
}
//计算总价,只计算选中的商品
//生成订单,入库:order和order_item,事务
Order order = buildOrder(uid, orderNo, shippingId, orderItemList);
int rowForOrder = orderMapper.insertSelective(order);
if (rowForOrder <= 0) {
return ResponseVo.error(ResponseEnum.ERROR);
}
int rowForOrderItem = orderItemMapper.batchInsert(orderItemList);
if (rowForOrderItem <= 0) {
return ResponseVo.error(ResponseEnum.ERROR);
}
//更新购物车(选中的商品)
//Redis有事务(打包命令),不能回滚
for (Cart cart : cartList) {
cartService.delete(uid, cart.getProductId());
}
//构造orderVo
OrderVo orderVo = buildOrderVo(order, orderItemList, shipping);
return ResponseVo.success(orderVo);
}
@Override
public ResponseVo<PageInfo> list(Integer uid, Integer pageNum, Integer pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<Order> orderList = orderMapper.selectByUid(uid);
Set<Long> orderNoSet = orderList.stream()
.map(Order::getOrderNo)
.collect(Collectors.toSet());
List<OrderItem> orderItemList = orderItemMapper.selectByOrderNoSet(orderNoSet);
Map<Long, List<OrderItem>> orderItemMap = orderItemList.stream()
.collect(Collectors.groupingBy(OrderItem::getOrderNo));
Set<Integer> shippingIdSet = orderList.stream()
.map(Order::getShippingId)
.collect(Collectors.toSet());
List<Shipping> shippingList = shippingMapper.selectByIdSet(shippingIdSet);
Map<Integer, Shipping> shippingMap = shippingList.stream()
.collect(Collectors.toMap(Shipping::getId, shipping -> shipping));
List<OrderVo> orderVoList = new ArrayList<>();
for (Order order : orderList) {
OrderVo orderVo = buildOrderVo(order,
orderItemMap.get(order.getOrderNo()),
shippingMap.get(order.getShippingId()));
orderVoList.add(orderVo);
}
PageInfo pageInfo = new PageInfo<>(orderList);
pageInfo.setList(orderVoList);
return ResponseVo.success(pageInfo);
}
@Override
public ResponseVo<OrderVo> detail(Integer uid, Long orderNo) {
Order order = orderMapper.selectByOrderNo(orderNo);
if (order == null || !order.getUserId().equals(uid)) {
return ResponseVo.error(ResponseEnum.ORDER_NOT_EXIST);
}
Set<Long> orderNoSet = new HashSet<>();
orderNoSet.add(order.getOrderNo());
List<OrderItem> orderItemList = orderItemMapper.selectByOrderNoSet(orderNoSet);
Shipping shipping = shippingMapper.selectByPrimaryKey(order.getShippingId());
OrderVo orderVo = buildOrderVo(order, orderItemList, shipping);
return ResponseVo.success(orderVo);
}
@Override
public ResponseVo cancel(Integer uid, Long orderNo) {
Order order = orderMapper.selectByOrderNo(orderNo);
if (order == null || !order.getUserId().equals(uid)) {
return ResponseVo.error(ResponseEnum.ORDER_NOT_EXIST);
}
//只有[未付款]订单可以取消,看自己公司业务
if (!order.getStatus().equals(OrderStatusEnum.NO_PAY.getCode())) {
return ResponseVo.error(ResponseEnum.ORDER_STATUS_ERROR);
}
order.setStatus(OrderStatusEnum.CANCELED.getCode());
order.setCloseTime(new Date());
int row = orderMapper.updateByPrimaryKeySelective(order);
if (row <= 0) {
return ResponseVo.error(ResponseEnum.ERROR);
}
return ResponseVo.success();
}
@Override
public void paid(Long orderNo) {
Order order = orderMapper.selectByOrderNo(orderNo);
if (order == null) {
throw new RuntimeException(ResponseEnum.ORDER_NOT_EXIST.getDesc() + "订单id:" + orderNo);
}
//只有[未付款]订单可以变成[已付款]
if (!order.getStatus().equals(OrderStatusEnum.NO_PAY.getCode())) {
throw new RuntimeException(ResponseEnum.ORDER_STATUS_ERROR.getDesc() + "订单id:" + orderNo);
}
order.setStatus(OrderStatusEnum.PAID.getCode());
order.setPaymentTime(new Date());
int row = orderMapper.updateByPrimaryKeySelective(order);
if (row <= 0) {
throw new RuntimeException("将订单更新为已支付状态失败,订单id:" + orderNo);
}
}
private OrderVo buildOrderVo(Order order, List<OrderItem> orderItemList, Shipping shipping) {
OrderVo orderVo = new OrderVo();
BeanUtils.copyProperties(order, orderVo);
List<OrderitemVo> OrderItemVoList = orderItemList.stream().map(e -> {
OrderitemVo orderItemVo = new OrderitemVo();
BeanUtils.copyProperties(e, orderItemVo);
return orderItemVo;
}).collect(Collectors.toList());
// orderVo.setOrderItemVoList(OrderItemVoList);
if (shipping != null) {
orderVo.setShippingId(shipping.getId());
orderVo.setShippingVo(shipping);
}
return orderVo;
}
private Order buildOrder(Integer uid,
Long orderNo,
Integer shippingId,
List<OrderItem> orderItemList
) {
BigDecimal payment = orderItemList.stream()
.map(OrderItem::getTotalPrice)
.reduce(BigDecimal.ZERO, BigDecimal::add);
Order order = new Order();
order.setOrderNo(orderNo);
order.setUserId(uid);
order.setShippingId(shippingId);
order.setPayment(payment);
order.setPaymentType(PaymentTypeEnum.PAY_ONLINE.getCode());
order.setPostage(0);
order.setStatus(OrderStatusEnum.NO_PAY.getCode());
return order;
}
private Long generateOrderNo() {
return System.currentTimeMillis() + new Random().nextInt(999);
}
private OrderItem buildOrderItem(Integer uid, Long orderNo, Integer quantity, Product product) {
OrderItem item = new OrderItem();
item.setUserId(uid);
item.setOrderNo(orderNo);
item.setProductId(product.getId());
item.setProductName(product.getName());
item.setProductImage(product.getMainImage());
item.setCurrentUnitPrice(product.getPrice());
item.setQuantity(quantity);
item.setTotalPrice(product.getPrice().multiply(BigDecimal.valueOf(quantity)));
return item;
}
}
项目打包命令
mvn clean package
打包跳过单侧
mvn clean package -Dmaven.test.skip=true;