SpringBoot----后端全接口实现实现商城管理系统

SpringBoot后端实现全接口实现商城管理系统

技术栈

后端使用的是SpringBoot+mybaris+maven+RabbitMQ(进行异步通知)+支付接口(微信和支付宝均采用的是Native支付)

SpringBoot----后端全接口实现实现商城管理系统_第1张图片

  • 支付文本转换为二维码 利用浏览进行转换 避免后端代码实现 减少服务器压力

因为整体的执行流程的需要,支付板块被单独分隔开,所以此处支付是独立系统,在下一篇文章进行更新说明。

本次的所有的后端业务接口均是单例测试Service层,所有的后端API接口是通过Postman进行测试的。

项目结构
SpringBoot----后端全接口实现实现商城管理系统_第2张图片
对应文件结构
Vo文件 对应给前端返回格式
form文件 对应前端给后端传入参数的格式
exception文件 是对运行时的异常进行处理 返回提示消息给前端
enums文件 枚举字段类
UserLoginInterceptor 对应的拦截器
InterceptorConfig 对应的拦截器进行配置 主要就是对cooike中的sessioid进行见检查

数据库的表结构

SpringBoot----后端全接口实现实现商城管理系统_第3张图片

环境配置使用的是
pom.xml文件


<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.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.1.7.RELEASEversion>
        <relativePath/> 
    parent>
    <groupId>com.dsqgroupId>
    <artifactId>mallartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>mallname>
    <description>Demo project for Spring Bootdescription>
    <properties>
        <java.version>1.8java.version>
    properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
        dependency>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
            <scope>testscope>
        dependency>

        <dependency>
            <groupId>org.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starterartifactId>
            <version>2.1.0version>
        dependency>

        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
        dependency>
        <dependency>
            <groupId>org.junit.jupitergroupId>
            <artifactId>junit-jupiterartifactId>
            <version>RELEASEversion>
            <scope>testscope>
        dependency>

        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
        dependency>

        <dependency>
            <groupId>com.github.pagehelpergroupId>
            <artifactId>pagehelper-spring-boot-starterartifactId>
            <version>1.2.13version>
        dependency>


        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
        dependency>


        <dependency>
            <groupId>com.google.code.gsongroupId>
            <artifactId>gsonartifactId>
        dependency>
        
    dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>

            <plugin>
                <groupId>org.mybatis.generatorgroupId>
                <artifactId>mybatis-generator-maven-pluginartifactId>
                <version>1.3.7version>


                <configuration>
                    <overwrite>trueoverwrite>
                configuration>
            plugin>

        plugins>
    build>

project>

Lombok

  1. 该插件可以自动打入get和set方法
  2. 需要在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是不同的

  1. Localhost
  2. 127.0.0.1

Ip和域名不一样 都算是跨域 对应存储的cookie是不相同的

SpringBoot----后端全接口实现实现商城管理系统_第4张图片

本次的数据库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


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">

        
        <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin" />



        <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>

        <javaModelGenerator targetPackage="com.dsq.mall.pojo" targetProject="src/main/java">

            <property name="enableSubPackages" 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

对应的方法和实现类

SpringBoot----后端全接口实现实现商城管理系统_第5张图片

整代码的逻辑核心层都在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语句进行判断的时候 只对确定性的条件进行判断
就会出现·多重选择

SpringBoot----后端全接口实现实现商城管理系统_第6张图片

购物车模块的开发

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;

你可能感兴趣的:(后端,后端,spring,boot,java)