用户下单、订单支付(day08下)

用户下单

需求分析和设计

用户下单业务说明:

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

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

用户点餐业务流程:
接口设计(分析):

接口设计:

数据库设计:

用户下单业务对应的数据表为 orders 表和 order_detail 表(一对多关系,一个订单关联多个订单明细):

数据库设计:订单表 orders

数据库设计:订单明细表 order_detail

代码开发

根据用户下单接口的参数设计DTO:

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

创建OrderController并提供用户下单方法:

创建OrderService接口,并声明用户下单方法:

创建OrderServiceImpl实现OrderService接口(1):

package com.sky.service.impl;

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

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

/**
 * @author 石头
 * @version 1.0
 */
@Service
public class OrderServiceImp implements OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private ShoppingCartMapper shoppingCartMapper;
    @Autowired
    private AddressBookMapper addressBookMapper;

    /**
     * 用户下单
     * @param ordersSubmitDTO
     * @return
     */
    @Transactional
    @Override
    public OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO) {

        //处理各种业务异常(地址簿为空,购物车为空)
        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);
        }

        //构造订单数据
        Orders order = new Orders();
        BeanUtils.copyProperties(ordersSubmitDTO,order);
        order.setPhone(addressBook.getPhone());
        order.setAddress(addressBook.getDetail());
        order.setConsignee(addressBook.getConsignee());
        order.setNumber(String.valueOf(System.currentTimeMillis()));
        order.setUserId(userId);
        order.setStatus(Orders.PENDING_PAYMENT);
        order.setPayStatus(Orders.UN_PAID);
        order.setOrderTime(LocalDateTime.now());
        //向订单表插入1条数据
        orderMapper.insert(order);

        //向订单明细插入n条数据
        List<OrderDetail> orderDetailList = new ArrayList<>();
        for (ShoppingCart cart : shoppingCartList) {
            OrderDetail orderDetail = new OrderDetail();//订单明细数据
            BeanUtils.copyProperties(cart, orderDetail);
            orderDetail.setOrderId(order.getId());//设置当前订单明细关联的订单id
            orderDetailList.add(orderDetail);
        }
        //向明细表插入n条数据
        orderDetailMapper.insertBatch(orderDetailList);

        //清空当前用户的购物车数据
        shoppingCartMapper.deleteByUserId(userId);

        //封装返回结果
        OrderSubmitVO orderSubmitVO = OrderSubmitVO.builder()
                .id(order.getId())
                .orderNumber(order.getNumber())
                .orderAmount(order.getAmount())
                .orderTime(order.getOrderTime())
                .build();
        return orderSubmitVO;
    }
}

创建OrderMapper接口和对应的xml映射文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<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>

创建OrderDetailMapper接口和对应的xml映射文件: 

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<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>

功能测试

订单支付

微信支付介绍

要实现微信支付就需要注册微信支付的一个商户号,这个商户号是必须要有一家企业并且有正规的营业执照。只有具备了这些资质之后,才可以去注册商户号,才能开通支付权限。

微信支付产品:

参考:产品中心 - 微信支付商户平台

微信支付接入流程:

微信小程序支付时序图:

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

JSAPI下单(对应时序图的第5步):商户系统调用该接口在微信支付服务后台生成 预支付交易单
接口地址:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml

微信支付-开发者文档

微信小程序调起支付:通过JSAPI下单接口获取到发起支付的必要参数prepay_id,然后使用微信支付提供的小程序方法调起小程序支付
微信支付-开发者文档
微信小程序调起支付(对应时序图的第10步)

调用wx.requestPayment(OBJECT)发起微信支付

微信支付准备工作

微信小程序支付时序图:

如何保证数据安全?

完成微信支付有两个关键的步骤:

  1. 第一个就是需要在商户系统当中调用微信后台的一个下单接口,就是生成预支付交易单。
  2. 第二个就是支付成功之后微信后台会给推送消息。

这两个接口数据的安全性,要求其实是非常高的。
解决: 微信提供的方式就是对数据进行加密、解密、签名多种方式。要完成数据加密解密,需要提前准备相应的一些文件,其实就是一些证书。

获取微信支付平台证书、商户私钥文件:

 如何调用到商户系统?

微信后台会调用到商户系统给推送支付的结果,在这里我们就会遇到一个问题,就是微信后台怎么就能调用到我们这个商户系统呢?因为这个调用过程,其实本质上也是一个HTTP请求。

目前,商户系统它的ip地址就是当前自己电脑的ip地址,只是一个局域网内的ip地址,微信后台无法调用到。

解决:内网穿透。通过cpolar软件 可以获得一个临时域名,而这个临时域名是一个公网ip,这样,微信后台就可以请求到商户系统了。

cpolar软件的使用:

获取临时域名:支付成功后微信服务通过该域名回调我们的程序

1). 下载与安装
下载地址:https://dashboard.cpolar.com/get-started

2). cpolar指定authtoken
复制authtoken:

3). 获取临时域名

4). 验证临时域名有效性
访问接口文档
https://544d85d1.r17.cpolar.top/doc.html#/home

代码导入

微信支付相关配置: notify/psySuccess对应的某个controller的路径设置

导入微信支付功能代码:

功能测试

上一节:

地址簿功能代码(day08上)-CSDN博客

下一节:

订单支付-跳过微信支付(day08)-CSDN博客

### 关于苍穹外卖 Day06 的 Mac 环境教程 目前提供的引用资料涵盖了从环境搭建到登录功能实现的内容,但并未直接提及 **Day06** 的具体教学内容。然而,基于已有的参考资料[^1][^2][^3][^4],可以推测该项目可能涉及更高级的功能开发或优化,例如订单管理、支付集成、权限控制等。 以下是关于 **苍穹外卖 Day06** 可能的教学重点及其在 Mac 系统上的适配建议: #### 一、潜在的技术点分析 根据项目的整体进度推断,Day06 很可能会覆盖以下技术领域之一或多者: - **订单管理系统**: 实现下单、取消订单等功能。 - **支付接口集成**: 如微信支付支付支付的 SDK 集成。 - **性能优化**: 使用 Redis 缓存提升系统响应速度。 - **分布式事务处理**: 解决跨服务数据一致性问题。 - **前端页面增强**: 完善用户界面交互设计。 这些功能通常会涉及到后端逻辑扩展和数据库结构调整。 #### 二、Mac 环境下的注意事项 对于运行在 Mac 特别是搭载 Apple Silicon (如 M3 芯片) 上的应用程序来说,需要注意以下几个方面: 1. **JDK版本兼容性** - 苍穹外卖项目推荐使用 JDK 17 或以上版本,在安装 OpenJDK/JetBrains Runtime for macOS ARM 架构时需确认其支持情况。 2. **MySQL/MariaDB 数据库设置** - 如果新增加了业务需求(比如订单详情),则需要调整对应的 SQL 结构并同步更新 MyBatis 映射文件中的字段定义。 ```sql ALTER TABLE `order` ADD COLUMN delivery_address VARCHAR(255); ``` 3. **Redis缓存配置** - 当引入 Redis 来存储高频访问的数据项时,请确保本地已经正确启动 redis-server 并修改 application.yml 中的相关参数。 ```yaml spring: redis: host: localhost port: 6379 timeout: 5000ms ``` 4. **Nginx反向代理与负载均衡** - 继续沿用之前提到过的 Nginx 设置来分流请求流量至不同的微服务实例上。 ```nginx upstream backend { server 127.0.0.1:8081; server 127.0.0.1:8082; } server { listen 80; location /api/ { proxy_pass http://backend/; } } ``` 5. **解决常见错误提示** - 若遇到类似 “Port already in use” 错误,则按照先前描述的方法定位冲突进程号并通过 taskkill 命令终止它: ```bash lsof -i :8080 kill $(lsof -t -i :8080) ``` #### 三、代码片段展示 假设第六天的任务围绕着订单创建展开,那么下面是一个简单的 Controller 方法用于接收 POST 请求并将新记录保存进数据库的例子: ```java @RestController @RequestMapping("/orders") public class OrderController { @Autowired private OrderService orderService; /** * 创建新的订单条目. * * @param dto 订单信息对象 * @return 成功与否的状态码及消息 */ @PostMapping("") public ResponseEntity<String> createOrder(@RequestBody CreateOrderDTO dto){ try{ Long orderId = orderService.create(dto); return new ResponseEntity<>("Created successfully with ID:" +orderId, HttpStatus.CREATED); }catch(Exception e){ return new ResponseEntity<>(e.getMessage(),HttpStatus.INTERNAL_SERVER_ERROR); } } } ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值