【烘焙坊项目】后端搭建(10) - 地址簿功能&用户下单&微信支付

一、地址簿功能

1.1需求分析和设计

业务功能及对应接口设计

查询地址列表 查询当前登录用户的所有地址信息

接口文档:

新增地址 新增地址

接口文档:

修改地址 根据id查询地址 & 根据id修改地址

根据id查询地址接口文档:

根据id修改地址接口文档:

删除地址 根据id删除地址

接口文档:

设置默认地址 设置默认地址

接口文档:

查询默认地址 查询默认地址

接口文档:

1.2代码开发

1.2.1新增地址

controller层

service层

dao层

1.2.2查询登录用户所有的地址

controller层

service层

dao层

1.2.3根据id查询地址

controller层

service层

dao层

1.2.4根据id修改地址

controller层

service层

dao层

1.2.5根据id删除地址

controller层

service层

dao层

1.2.6设置默认地址

controller层

service层

dao层

update条件有id,更改传入的具体地址的默认属性

1.2.7查询默认地址

controller层

service层

dao层

使用 查询登录用户所有地址 的mapper接口

1.3功能测试

二、用户下单

2.1需求分析和设计

用户下单业务说明:

在电商系统中,用户是通过下单的方式通知商家,用户已经购买了商品,需要商家进行备货和发货。

用户下单后会产生订单相关数据,订单数据需要能够体现如下信息:

1.买了哪些商品?每个商品数量是多少?

2.订单总金额是多少?

3.收货地址是哪?

4.哪个用户下的单

5.用户的手机号是多少

查看接口文档:

2.2代码开发

controller层

service层

java 复制代码
import com.cake.constant.MessageConstant;
import com.cake.context.BaseContext;
import com.cake.dto.OrdersSubmitDTO;
import com.cake.entity.AddressBook;
import com.cake.entity.OrderDetail;
import com.cake.entity.Orders;
import com.cake.entity.ShoppingCart;
import com.cake.exception.AddressBookBusinessException;
import com.cake.exception.ShoppingCartBusinessException;
import com.cake.mapper.AddressBookMapper;
import com.cake.mapper.OrderDetailMapper;
import com.cake.mapper.OrderMapper;
import com.cake.mapper.ShoppingCartMapper;
import com.cake.service.OrderService;
import com.cake.vo.OrderSubmitVO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private AddressBookMapper addressBookMapper;
    @Autowired
    private ShoppingCartMapper shoppingCartMapper;
    /**
     * 用户下单
     * @param ordersSubmitDTO
     * @return
     */
    @Override
    public OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO) {

        //1.处理各种业务异常(地址簿如果为空,购物车数据为空)
            //查询地址簿数据
        AddressBook addressBook = addressBookMapper.getById(ordersSubmitDTO.getAddressBookId());
        if(addressBook==null){
            //抛出业务异常
            throw new AddressBookBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);
        }
            //查询购物车数据
        Long userId = BaseContext.getCurrentId();
        ShoppingCart shoppingCart = new ShoppingCart();
        shoppingCart.setUserId(userId);
        List<ShoppingCart> shoppingCartList = shoppingCartMapper.list(shoppingCart);
        if(shoppingCartList ==null || shoppingCartList.size()==0){
            //抛出业务异常
            throw new ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);
        }
        //2.向订单表插入1条数据
        Orders orders = new Orders();
        BeanUtils.copyProperties(ordersSubmitDTO,orders);
        orders.setOrderTime(LocalDateTime.now());
        orders.setPayStatus(Orders.UN_PAID);
        orders.setStatus(Orders.PENDING_PAYMENT);
        orders.setNumber(String.valueOf(System.currentTimeMillis()));//时间戳
        orders.setPhone(addressBook.getPhone());
        orders.setConsignee(addressBook.getConsignee());//收货人
        orders.setUserId(userId);
        orders.setAddress(addressBook.getDetail());

        orderMapper.insert(orders);
        //3.向订单明细表插入n条数据
        List<OrderDetail> orderDetailList = new ArrayList<>();
        for (ShoppingCart cart:shoppingCartList){
            OrderDetail orderDetail = new OrderDetail();//订单明细
            BeanUtils.copyProperties(cart,orderDetail);
            orderDetail.setOrderId(orders.getId());//设置当前订单明细关联的订单id
            orderDetailList.add(orderDetail);
        }
            orderDetailMapper.insertBatch(orderDetailList);
        //4.清空当前用户购物车数据
        shoppingCartMapper.clean(userId);
        //5.封装VO并返回结果
        OrderSubmitVO orderSubmitVO = OrderSubmitVO.builder()
                .id(orders.getId())
                .orderTime(orders.getOrderTime())
                .orderNumber(orders.getNumber())
                .orderAmount(orders.getAmount())
                .build();
        return orderSubmitVO;
    }
}

dao层

2.3功能测试

通过

三、订单支付

3.1微信支付介绍

微信支付产品:

付款码支付,JSAPI支付,小程序支付,Native支付,App支付,刷脸支付,掌纹支付

参考:https://pay.weixin.qq.com/static/product/product_index.shtml

微信支付接入流程:

微信小程序支付时序图:

JSAPI下单 :商户系统调用该接口在微信支付服务后台生成的预支付订单

适用对象:直连商户

请求方式:POST

返回参数

|-----------|-----------|----------------|----|---------------------------------|
| 参数名 | 变量 | 类型[长度限制] | 必填 | 描述 |
| 预支付交易会话标识 | prepay_id | string[1,64] | 是 | 预支付交易会话标识。用于后续接口调用中使用,该值有效期为2小时 |

微信小程序调起支付:通过JSAPI下单接口获取到发起支付的必要参数prepay_id,然后使用微信支付提供的小程序方法调起小程序支付

官网:https://pay.weixin.qq.com/doc/v3/merchant/4012791898

3.2代码开发 - 跳过微信支付

由于微信支付需要商家认证才能开通,个人开发者只能模拟支付的流程,完成后续项目的学习

步骤一:打开小程序开发工具,进行修改

步骤二:加入微信支付部分代码

controller层

service层

java 复制代码
/**
     * 订单支付
     * @param ordersPaymentDTO
     * @return
     * @throws Exception
     */
    public OrderPaymentVO payment(OrdersPaymentDTO ordersPaymentDTO) throws Exception {
       /*
        //当前登录用户id
        Long userId = BaseContext.getCurrentId();
        User user = userMapper.getById(userId);

        //调用微信支付接口,生成预支付交易订单
        JSONObject jsonObject = weChatPayUtil.pay(
                ordersPaymentDTO.getOrderNumber(),//商户订单号
                new BigDecimal(0.01),//支付金额,单位 元
                "甜慕烘焙订单",//商品描述
                user.getOpenid()//微信用户的openid
        );

        if(jsonObject.getString("code") != null && jsonObject.getString("code").equals("ORDERPAID")){
            throw new OrderBusinessException("该订单已支付");
        }

        OrderPaymentVO vo = jsonObject.toJavaObject(OrderPaymentVO.class);
        vo.setPackageStr(jsonObject.getString("package"));
        */
        log.info("跳过微信支付,支付成功");
        paySuccess(ordersPaymentDTO.getOrderNumber());
        return new OrderPaymentVO();
    }

    /**
     * 支付成功,修改订单状态
     * @param outTradeNo
     */
    public void paySuccess(String outTradeNo) {
        //当前登录用户id
        Long userId = BaseContext.getCurrentId();
        //根据订单号查询当前用户的订单
        Orders ordersDB = orderMapper.getByNumberAndUserId(outTradeNo,userId);
        //根据订单id更新订单状态,支付方式,支付状态,结账时间
        Orders orders = Orders.builder()
                .id(ordersDB.getId())
                .status(Orders.TO_BE_CONFIRMED)
                .payStatus(Orders.PAID)
                .checkoutTime(LocalDateTime.now())
                .build();

        orderMapper.update(orders);
    }

3.3功能测试

通过

四、小结

由于是个人开发所以无法实现微信的支付功能。这一部分又确实比较难,所以我对逻辑进行了梳理。

先由controller层接收到前端发起的支付请求,调用service层方法。

service层 1.先获取当前用户id并查到user数据。 2.再调用weChatPayUtil.pay()(微信支付工具),传入 商户订单号,支付金额,商品描述和用户的openid

在weChatPayUtil.pay()中 1.先调用jsapi()方法并传入商户订单号,支付金额,商品描述和用户的openid

在jsapi()方法中 1.先创建jsonObject,并放入appid,mchid(商户号),description(商品描述),orderNum,notify_url(支付成功的回调地址)。 2.新建一个JSONObject类型amount,将传入的支付金额进行乘100,四舍五入保留两位小数,转为整数最后加上"CNY"的一系列操作。 3.创建JSONObject类型payer并放入openid。 4.将amount和payer都放入jsonObject并将jsonObject序列化为JSON格式字符串body 5.调用post方法并传入body和JSAPI(地址)

在post()中 1.调用getClient()创建HTTP客户端实例httpClient

在getClient()中 1.使用PemUtil工具类加载商户私钥和商户证书,如下例。 2.将证书封装为List格式wechatPayCertificates。 3.创建WechatPayHttpClientBuilder类型builder,并传入商户信息和平台证书列表 4.创建 closeableHttpClient类型httpClient,并传入builder的数据 5.将httpClient返回给调用方(post)

java 复制代码
merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(new File(weChatProperties.getPrivateKeyFilePath())));
X509Certificate x509Certificate = PemUtil.loadCertificate(new FileInputStream(new File(weChatProperties.getWeChatPayCertFilePath())));

在post()中 2.构造HttpPost类型的httpPost,并设置url,响应数据格式要求,请求数据格式,微信支付专用验签头和请求体。 3.传入httpPost执行post请求并接收微信支付服务器返回的结果 4.将返回的response转为JSON格式字符串并返回给jsapi() 5.关闭HTTP客户端与响应对象

在jsapi()中 6.接收post()返回的response并返回给Pay()

在weChatPayUtil.pay()中 2.接收到返回的response并解析返回结果,拿到prepayId 3.判断prepayId不为空的情况下生成时间戳,随机字符,并组装成签名参数列表list 4.再构造stringBuilder并遍历list中数据将其组建为String类型的signMessage再转为byte[]格式 5.获取SHA256withRSA算法的签名对象,并初始化签名再传入待签名数据最后执行签名并进行Base64编码 6.构建JSONObject类型数据,传入timeStamp(时间戳),nonceStr(随机字符串),package(预支付数据包),signType(签名类型),paySign(支付签名),并返回数据给service层

service层 3.判断返回的数据中的code判断状态,如果存在且值为ORDER_PAID说明该订单已支付过,抛出异常。 4.将返回的数据反序列化为OrderPaymentVO 并将对应参数传入。 5. 根据订单号查询当前用户的订单,并调用Mapper层修改订单id的订单状态,支付方式,支付状态和结账时间 6.返回OrderPaymentVO

controller层返回给前端OrderPaymentVO

相关推荐
invicinble5 小时前
这里对java的知识体系做一个全域的介绍
java·开发语言·python
小码哥_常5 小时前
MyBatis-Plus:让数据库操作飞起来的神器
后端
wbs_scy6 小时前
【Linux 线程进阶】进程 vs 线程资源划分 + 线程控制全详解
java·开发语言
ss2736 小时前
食谱推荐系统功能测试如何写?
java·数据库·spring boot·功能测试
2301_811274316 小时前
基于SpringBoot的智能家居管理系统
spring boot·后端·智能家居
AI人工智能+电脑小能手6 小时前
【大白话说Java面试题】【Java基础篇】第15题:JDK1.7中HashMap扩容为什么会发生死循环?如何解决
java·开发语言·数据结构·后端·面试·哈希算法
舒一笑6 小时前
我把设备指纹生成逻辑拆开了:它到底凭什么区分不同设备?
后端·程序员·掘金技术征文
l1t6 小时前
DeepSeek总结的数据库外部表
数据库
m0_674294646 小时前
如何编写SQL存储过程性能对比_记录执行时间评估优化效果
jvm·数据库·python
try2find6 小时前
打印ascii码报错问题
java·linux·前端