苍穹外卖资源点整理+个人错误解析-Day08-用户下单,订单支付
流程:准备工作:1.调用预支付接口,如何保证数据安全?即对数据加密解密:获得微信平台证书和商户私钥2.微信后台如何调用到商户系统?
导入地址簿功能代码
分析
产品原型:

可以存储多个地址,但只能有一个默认地址。修改地址这个功能不能只是修改,还需要先进行数据回显。

接口设计:

新增:

查询所有地址:

查询默认地址:

修改:

删除:

根据id查询地址:

设置默认地址(本质是修改):

代码
查询所有地址
controller层:
1.根据接口文档,请求参数其实就是用户id,先获取用户id。通过创建一个对象来获取,其中对象除了获得的userid没有其他属性值了。
AddressBook addressBook=new AddressBook(); Long currentId = BaseContext.getCurrentId(); addressBook.setUserId(currentId);
通过userid进行查询:
List<AddressBook>list =addRessBookService.list(addressBook);
完整代码,泛型是AddressBook的list集合原因就在于返回的地址可能不止只有一个,并且返回类型要写。
serviceimpl:主要注意这是有返回结果的,需要定义,不定义就直接return+mapper.list
@Override
public List<AddressBook> list(AddressBook addressBook) {
List<AddressBook> list= addRessBookMapper.list(addressBook);
return list;
}
mapper使用动态查询:
<select id="list" parameterType="AddressBook" resultType="AddressBook">
select * from address_book
<where>
<if test="userId != null"> and user_id = #{userId} </if>
<if test="phone != null"> and phone = #{phone} </if>
<if test="isDefault != null"> and is_default = #{isDefault} </if>
</where>
</select>
新增地址
controller:
/**
* 添加地址
*/
@PostMapping
@ApiOperation("新增地址")
public Result add(@RequestBody AddressBook addressBook){
addressBookService.save(addressBook);
return Result.success();
}
实现层:
一定要获取用户id,这是保证加到哪个用户的购物车地址
并且不能设置为默认地址。
addressBook.setUserId(BaseContext.getCurrentId())
用户身份绑定:从 BaseContext 上下文中获取当前登录用户的ID,并将其设置到 addressBook 对象中
数据归属:确保每条地址记录都能关联到正确的用户,实现用户数据隔离
addressBook.setIsDefault(0)
默认状态初始化:将新增地址设置为非默认地址(0表示非默认)
业务规则:新添加的地址默认不作为用户的默认收货地址,符合常规业务逻辑
/**
* 添加地址
*/
@Override
public void save(AddressBook addressBook) {
addressBook.setUserId (BaseContext.getCurrentId());
addressBook.setIsDefault(0);//设置成为非默认
addressBookMapper.save(addressBook);
}
mapper:
@Insert("insert into address_book" +
" (user_id, consignee, phone, sex, province_code, province_name, city_code, city_name, district_code," +
" district_name, detail, label, is_default)" +
" values (#{userId}, #{consignee}, #{phone}, #{sex}, #{provinceCode}, #{provinceName}, #{cityCode}, #{cityName}," +
" #{districtCode}, #{districtName}, #{detail}, #{label}, #{isDefault})")
void save(AddressBook addressBook);
根据id进行查询
controller:
@GetMapping("/{id}")
@ApiOperation("根据id进行查询")
public Result<AddressBook> getById(@PathVariable Long id){
AddressBook addressBook = addressBookService.getById(id);
return Result.success(addressBook);
}
serviceimpl
/**
* 根据id查询
* @param id
* @return
*/
@Override
public AddressBook getById(Long id) {
AddressBook addressBook= addressBookMapper.getById(id);
return addressBook;
}
mapper
/**
* 根据id查询
* @param id
* @return
*/
@Select("select * from address_book where id = #{id}")
AddressBook getById(Long id);
根据id进行修改
controller
/**
* 根据id修改地址
*/
@PutMapping
@ApiOperation("修改地址")
public Result update(@RequestBody AddressBook addressBook){
addressBookService.update(addressBook);
return Result.success();
}
serviceimpl
/**
* 根据id进行修改
* @param addressBook
*/
@Override
public void update(AddressBook addressBook) {
addressBookMapper.update(addressBook);
}
mapper
<update id="update" parameterType="addressBook">
update address_book
<set>
<if test="consignee != null"> consignee = #{consignee}, </if>
<if test="sex != null"> sex = #{sex}, </if>
<if test="phone != null"> phone = #{phone}, </if>
<if test="detail != null"> detail = #{detail}, </if>
<if test="label != null"> label = #{label}, </if>
<if test="isDefault != null"> is_default = #{isDefault}, </if>
</set>
where id = #{id}
</update>
设置默认地址
@PutMapping("/default")
@ApiOperation("设置默认地址")
public Result setDefault(@RequestBody AddressBook addressBook){
addressBookService.setDefault(addressBook);
return Result.success();
}
@Transactional
@Override
public void setDefault(AddressBook addressBook) {
//1、将当前用户的所有地址修改为非默认地址 update address_book set is_default = ? where user_id = ?
addressBook.setIsDefault(0);
addressBook.setUserId(BaseContext.getCurrentId());
addressBookMapper.updateIsDefaultByUserId(addressBook);
//2、将当前地址改为默认地址 update address_book set is_default = ? where id = ?
addressBook.setIsDefault(1);
addressBookMapper.update(addressBook);
}
@Update("update address_book set is_default = #{isDefault} where user_id = #{userId}")
void updateIsDefaultByUserId(AddressBook addressBook);
根据id删除
/**
* 根据id删除
*/
@DeleteMapping
@ApiOperation("根据id删除地址")
public Result deleteById(Long id) {
addressBookService.deleteById(id);
return Result.success();
}
/**
* 根据id删除地址
*
* @param id
*/
public void deleteById(Long id) {
addressBookMapper.deleteById(id);
}
/**
* 根据id删除地址
* @param id
*/
@Delete("delete from address_book where id = #{id}")
void deleteById(Long id);
用户下单
分析

买的哪些商品和数量可以通过购物车模块中的查询购物车获得。
订单总金额需要通过程序获取,有两部分:菜品费用+其他费用(餐盒费配送费等等)。
其他三个疑问都可以在地址簿中解决。
用户点餐业务流程:

接口分析:


接口文档:
数据库设计:

代码
1.controller层
根据接口文档的返回数据可得,需要使用一个vo类进行封装:
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping("/submit")
@ApiOperation("用户下单")
public Result<OrderSubmitVO> submit(@RequestBody OrdersSubmitDTO ordersSubmitDTO){
OrderSubmitVO orderSubmitVO=orderService.submitOrder(ordersSubmitDTO);
return Result.success(orderSubmitVO);
}
2.serviceimpl层
要实现用户下单的话需要实现:
1.向订单表插入一条数据
2.向订单明细表插入n条数据
3.清空购物车(下单成功后)
4.返回vo数据给前端
在实现这四步之前,有两个问题不能忽略,即用户收货地址有没有问题,用户有下单请求,但购物车为空又该怎么办。即需要处理业务异常:
//0.考虑并处理业务异常:
//异常1:地址簿为空不能下单
AddressBook addressBook = addressBookMapper.getById(ordersSubmitDTO.getAddressBookId());
if (addressBook==null){
throw new AddressBookBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);
}
//异常2:购物车为空不能下单
Long userId = BaseContext.getCurrentId();
//list方法需要传入一个shoppingcart对象
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);
}
1.向订单表插入一条数据:
order类:


有很多字段,在插入数据之前得封装数据,而有部分数据封装在dto,只需要进行数据拷贝:
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); orderMapper.insert(orders);
mapper.xml(需要返回主键值):
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into orders(user_id,number,status,address_book_id,order_time,checkout_time,pay_method,pay_status,amount,remark,user_name,phone,address,consignee,estimated_delivery_time,delivery_status,pack_amount,tableware_number,tableware_status)
values(#{userId},#{number},#{status},#{addressBookId},#{orderTime},#{checkoutTime},#{payMethod},#{payStatus},#{amount},#{remark},#{userName},#{phone},#{address},#{consignee},#{estimatedDeliveryTime},#{deliveryStatus},#{packAmount},#{tablewareNumber},#{tablewareStatus})
</insert>
2.向订单明细表插入n条数据:
//2.向订单明细表插入n条数据,订单明细就看购物车数据
List<OrderDetail> orderDetailList = new ArrayList<>();
for (ShoppingCart cart : shoppingCartList) {
OrderDetail orderDetail = new OrderDetail();///订单明细
BeanUtils.copyProperties(cart,orderDetail);
//设置该订单详情所属的订单 ID。这里使用的是刚刚插入订单表时生成的主键 ID(即 orders.getId()),建立订单和订单详情之间的关联关系。
orderDetail.setOrderId(orders.getId());//上面获取id就用在这里,设置当前订单明细关联的订单id
orderDetailList.add(orderDetail);
//在for循环里面写mapper方法就是单条插入
}
//在for循环外面写就是批量插入,也就有了定义List集合List<OrderDetail> orderDetailList = new ArrayList<>();
orderDetailMapper.insertBatch(orderDetailList);
xml:
<insert id="insertBatch">
insert into order_detail(name,image,order_id, dish_id, setmeal_id, dish_flavor, number, amount) values
<foreach collection="orderDetaillist" item="od" separator=",">
(#{od.name},#{od.image},#{od.orderId},#{od.dishId},#{od.setmealId},#{od.dishFlavor},#{od.number},#{od.amount})
</foreach>
</insert>
3.清空购物车(下单成功后):
shoppingCartMapper.deleteByUserId(userId);
4.返回vo数据给前端:
OrderSubmitVO orderSubmitVO = OrderSubmitVO.builder()
.id(orders.getId())
.orderNumber(orders.getNumber())
.orderAmount(orders.getAmount())
.build();
return orderSubmitVO;
订单支付
微信支付介绍

流程:


准备工作:
1.调用预支付接口,如何保证数据安全?
即对数据加密解密:
获得微信平台证书和商户私钥

2.微信后台如何调用到商户系统?

代码导入
进行支付相关配置:

填写格式如图:

代码讲解
流程图:

从申请微信支付开始,请求发送到ordercontroller中:

OrderPaymentDTO:

WeChatUtil这个工具类很重要,第五步调用是微信下单接口就与这个工具类有关。
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)