课程内容
导入地址簿
业务功能
- 查询地址列表
- 新增地址
- 修改地址
- 查询地址
- 删除地址
- 设置默认地址
- 查询默认地址
表设计
字段名 |
数据类型 |
说明 |
备注 |
id |
bigint |
主键 |
自增 |
user_id |
bigint |
用户id |
逻辑外键 |
consignee |
varchar(50) |
收货人 |
|
sex |
varchar(2) |
性别 |
|
phone |
varchar(11) |
手机号 |
|
province_code |
varchar(12) |
省份编码 |
|
province_name |
varchar(32) |
省份名称 |
|
city_code |
varchar(12) |
城市编码 |
|
city_name |
varchar(32) |
城市名称 |
|
district_code |
varchar(12) |
区县编码 |
|
district_name |
varchar(32) |
区县名称 |
|
detail |
varchar(200) |
详细地址信息 |
具体到门牌号 |
label |
varchar(100) |
标签 |
公司、家、学校 |
is_default |
tinyint(1) |
是否默认地址 |
1是 0否 |
代码导入
用户下单
需求分析
- 用户通过下单的方式通知商家
- 下单后产生订单相关数据
- 订单数据提供以下信息
- 买的商品类型 数量
- 订单总金额
- 收货地址 用户信息
表设计
-
用户下点业务对应的数据表分为订单表和订单明细表
- 订单表: 主要存储订单的基本信息(订单号 状态 金额 用户信息)
- 订单明细表: 主要存储订单详细信息(套餐 菜品)
-
orders订单表
字段名 |
数据类型 |
说明 |
备注 |
id |
bigint |
主键 |
自增 |
number |
varchar(50) |
订单号 |
|
status |
int |
订单状态 |
1待付款 2待接单 3已接单 4派送中 5已完成 6已取消 |
user_id |
bigint |
用户id |
逻辑外键 |
address_book_id |
bigint |
地址id |
逻辑外键 |
order_time |
datetime |
下单时间 |
|
checkout_time |
datetime |
付款时间 |
|
pay_method |
int |
支付方式 |
1微信支付 2支付宝支付 |
pay_status |
tinyint |
支付状态 |
0未支付 1已支付 2退款 |
amount |
decimal(10,2) |
订单金额 |
|
remark |
varchar(100) |
备注信息 |
|
phone |
varchar(11) |
手机号 |
冗余字段 |
address |
varchar(255) |
详细地址信息 |
冗余字段 |
consignee |
varchar(32) |
收货人 |
冗余字段 |
cancel_reason |
varchar(255) |
订单取消原因 |
|
rejection_reason |
varchar(255) |
拒单原因 |
|
cancel_time |
datetime |
订单取消时间 |
|
estimated_delivery_time |
datetime |
预计送达时间 |
|
delivery_status |
tinyint |
配送状态 |
1立即送出 0选择具体时间 |
delivery_time |
datetime |
送达时间 |
|
pack_amount |
int |
打包费 |
|
tableware_number |
int |
餐具数量 |
|
tableware_status |
tinyint |
餐具数量状态 |
1按餐量提供 0选择具体数量 |
字段名 |
数据类型 |
说明 |
备注 |
|
id |
bigint |
主键 |
自增 |
|
name |
varchar(32) |
商品名称 |
冗余字段 |
|
image |
varchar(255) |
商品图片路径 |
冗余字段 |
|
order_id |
bigint |
订单id |
逻辑外键 |
|
dish_id |
bigint |
菜品id |
逻辑外键 |
|
setmeal_id |
bigint |
套餐id |
逻辑外键 |
|
dish_flavor |
varchar(50) |
菜品口味 |
|
|
number |
int |
商品数量 |
|
|
amount |
decimal(10,2) |
商品单价 |
|
|
代码开发
设计DTO
- 根据用户下单解耦的参数设计DTO
- 在pojo模块 OrderSubmitDTO已定义
package com.sky.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
public class OrdersSubmitDTO implements Serializable {
//地址簿id
private Long addressBookId;
//付款方式
private int payMethod;
//备注
private String remark;
//预计送达时间
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime estimatedDeliveryTime;
//配送状态 1立即送出 0选择具体时间
private Integer deliveryStatus;
//餐具数量
private Integer tablewareNumber;
//餐具数量状态 1按餐量提供 0选择具体数量
private Integer tablewareStatus;
//打包费
private Integer packAmount;
//总金额
private BigDecimal amount;
}
设计VO
- 在pojo模块中 OrderSubmitVO已定义
package com.sky.vo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OrderSubmitVO implements Serializable {
//订单id
private Long id;
//订单号
private String orderNumber;
//订单金额
private BigDecimal orderAmount;
//下单时间
private LocalDateTime orderTime;
}
Controller层
- 创建OrderController并提供用户下单方法
@RestController("userOrder")
@RequestMapping("user/order")
@Api(tags = "用户端订单相关接口")
@Slf4j
public class OrderController {
@Autowired
private OrderService orderService;
@ApiOperation("用户下单")
@PostMapping("/submit")
public Result<OrderSubmitVO> submit(@RequestBody OrdersSubmitDTO ordersSubmitDTO){
log.info("用户下单:{}",ordersSubmitDTO);
OrderSubmitVO orderSubmitVO = orderService.submitOrder(ordersSubmitDTO);
return Result.success(orderSubmitVO);
}
}
Service层
- 创建OrderService接口 声明用户下单方法
public interface OrderService {
//用户下单
OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO);
}
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderDetailMapper orderDetailMapper;
//检查地址是否有误
@Autowired
private AddressBookMapper addressBookMapper;
//检查购物车是否为空
@Autowired
private ShoppingCartMapper shoppingCartMapper;
@Override
//用户下单
public OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO) {
//处理各种业务异常
AddressBook addressBook = addressBookMapper.getById(ordersSubmitDTO.getAddressBookId());
if (addressBook == null) {
throw new RuntimeException(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 RuntimeException(MessageConstant.SHOPPING_CART_IS_NULL);
}
//向订单表插入一条数据
Orders orders = new Orders();
BeanUtils.copyProperties(ordersSubmitDTO,orders);
orders.setUserId(userId);
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);
orderMapper.insert(orders);
List<OrderDetail> orderDetailList = new ArrayList<>();
//向订单明细表插入多条数据
for(ShoppingCart cart : shoppingCartlist){
OrderDetail orderDetail = new OrderDetail();
BeanUtils.copyProperties(cart,orderDetail);
orderDetail.setOrderId(orders.getId());
}
orderDetailMapper.insertBatch(orderDetailList);
//清空当前用户购物车数据
shoppingCartMapper.deleteById(userId);
//返回订单相关信息
OrderSubmitVO orderSubmitVO = OrderSubmitVO.builder()
.id(orders.getId())
.orderTime(orders.getOrderTime())
.orderNumber(orders.getNumber())
.orderAmount(orders.getAmount())
.build();
return orderSubmitVO;
}
Mapper层
@Mapper
public interface OrderMapper {
//插入订单数据
void insert(Orders orders);
}
<mapper namespace="com.sky.mapper.OrderMapper">
<insert id="insert" parameterType="Orders" useGeneratedKeys="true" keyProperty="id">
insert into orders
(number, status, user_id, address_book_id, order_time, checkout_time, pay_method, pay_status, amount, remark, phone, address, consignee, estimated_delivery_time, delivery_status, pack_amount, tableware_number, tableware_status) values (#{number}, #{status}, #{userId}, #{addressBookId}, #{orderTime}, #{checkoutTime}, #{payMethod}, #{payStatus}, #{amount}, #{remark}, #{phone}, #{address}, #{consignee}, #{estimatedDeliveryTime}, #{deliveryStatus}, #{packAmount}, #{tablewareNumber}, #{tablewareStatus}) </insert>
</mapper>
@Mapper
public interface OrderDetailMapper {
//批量插入订单数据
void insertBatch(List<OrderDetail> orderDetails);
}
<mapper namespace="com.sky.mapper.OrderDetailMapper">
<insert id="insertBatch" parameterType="list">
insert into order_detail
(name, order_id, dish_id, setmeal_id, dish_flavor, number, amount, image)
values
<foreach collection="orderDetails" item="od" separator=",">
(#{od.name},#{od.orderId},#{od.dishId},#{od.setmealId},#{od.dishFlavor},
#{od.number},#{od.amount},#{od.image})
</foreach>
</insert>
</mapper>
订单支付
- 实现微信支付需要注册微信商户号
- 个人不具备这种资质
- 了解微信支付的流程
- 能过阅读微信官方提供的接口文档
- 能够和第三方支付平台对接起来即可
- 代码固定可复用
微信支付介绍
- 微信支付包括多款产品
- 本项目选择小程序支付
- 接入流程
- 时序图
- 1.进入小程序下单
- 2.下单
- 3.返回订单号
- 4.申请微信支付
- 5.调用微信下单接口
- 6.返回预支付交易表示
- 7.将组合数据再次签名
- 8.返回支付参数
- 9.用户确认支付
- 12.显示支付结过
- 注:不同缩进分别对应
- 微信用户
- 微信支付相关接口
- JSAPI下单:
- 商户系统调用该接口在微信支付服务后台生成预支付交易单
- 对应时序图的第五步
- 微信小程序调起支付
- 通过JSAPI下单接口获取到发起支付的必要参数prepayid
- 然后通过微信支付提供的小程序方法调起小程序支付
- 对应时序图第十步
微信支付准备工作
数据安全问题
- 完成微信支付有两个关键的步骤
- 第一个需要在商户系统中调用后台的一个下单接口 就是生成预支付交易单
- 第二个就是支付成功之后微信后台会给推送消息
- 需要获得微信支付平台文件 商户私钥文件
如何调用到商户系统
- 微信后台会调用到商户系统推送支付的结过
- 调用本质上是http请求
- 目前我们电脑使用局域网ip 微信后台无法调用
- 使用内网穿透方案解决
cpolar软件
代码导入
- 直接导入黑马提供的支付代码即可
- 配置相关文件
- application-dev.yml
sky:
wechat:
appid: 自己的
secret: 自己的
mchid : 1561414331
mchSerialNo: 4B3B3DC35414AD50B1B755BAF8DE9CC7CF407606
privateKeyFilePath: D:\apiclient_key.pem
apiV3Key: CZBK51236435wxpay435434323FFDuv3
weChatPayCertFilePath: D:\wechatpay_166D96F876F45C7D07CE98952A96EC980368ACFC.pem
notifyUrl: https://www.weixin.qq.com/wxpay/pay.php
refundNotifyUrl: https://www.weixin.qq.com/wxpay/pay.php
sky:
wechat:
appid: ${sky.wechat.appid}
secret: ${sky.wechat.secret}
mchid : ${sky.wechat.mchid}
mchSerialNo: ${sky.wechat.mchSerialNo}
privateKeyFilePath: ${sky.wechat.privateKeyFilePath}
apiV3Key: ${sky.wechat.apiV3Key}
weChatPayCertFilePath: ${sky.wechat.weChatPayCertFilePath}
notifyUrl: ${sky.wechat.notifyUrl}
refundNotifyUrl: ${sky.wechat.refundNotifyUrl}
- 实际上我们没有黑马的证书文件
- 也无法自己申请
- 这部分代码实际上不能运行
- 阅读理解即可