一、地址簿功能
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
