
特别声明:本系列所涉及资料皆为黑马程序员课程中的资料
目录
一、前言
昨天没有更新,今天将昨天的部分也补偿上去,拆分项目所用的技术我们已经掌握的差不多了,接下来开始拆分剩下的模块。
二、用户部分
1、先将所用到的表导进去

2、创建user-service模块
还是默认直接创建就行了,创建完成后的目录是这样的

3、改造user-service模块
(1)pom文件
直接将cart-service中的拷贝过来就行了
<dependencies>
<!--common-->
<dependency>
<groupId>com.heima</groupId>
<artifactId>hm-common</artifactId>
<version>1.0.0</version>
</dependency>
<!-- api-->
<dependency>
<groupId>com.heima</groupId>
<artifactId>hm-api</artifactId>
<version>1.0.0</version>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-rsa</artifactId>
<version>1.0.9.RELEASE</version>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--nacos服务注册-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
(2)修改模块
这里教大家一个小技巧,把这两个勾选上可以自动导入包

整体来看就是这么多相关的包了 ,因为屏幕限制最后一个没截图上

这里刚才放错位置了

准备完成后开始启动项目
(3)yaml文件


(4)测试
运行前将这里设置为local

可以看到测试成功了
如果报错是数据库连接超时的话就将数据库地址改一下
可以在这改

也可以在这改
三、交易部分
交易部分和用户部分差不多
1、创建trade-service模块
还是直接默认创建即可,创建完成后将包创建出来

2、改造trade-service模块
(1)pom文件
还是和用户部分的pom文件一样

(2)修改模块
这是修改后的模块

(3)yaml文件

(4)改造代码
扣减库存部分代码
复制下这段代码

可以看到这里爆红了,原因是没找到这个类

直接将hm-service中的类拉过来

清理购物车商品
复制这段代码

改造完成后放在这里
改造trade-service
这个地方需要改造下

// 3.扣减库存
try {
itemClient.deductStock(detailDTOS);
} catch (Exception e) {
throw new RuntimeException("库存不足!");
}
// 4.清理购物车商品
cartClient.deleteCartItemByIds(itemIds);
return order.getId();
改造后的完整代码
package com.hmall.trade.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.api.client.CartClient;
import com.hmall.api.client.ItemClient;
import com.hmall.api.dto.ItemDTO;
import com.hmall.api.dto.OrderDetailDTO;
import com.hmall.common.exception.BadRequestException;
import com.hmall.common.utils.UserContext;
import com.hmall.trade.domain.dto.OrderFormDTO;
import com.hmall.trade.domain.po.Order;
import com.hmall.trade.domain.po.OrderDetail;
import com.hmall.trade.mapper.OrderMapper;
import com.hmall.trade.service.IOrderDetailService;
import com.hmall.trade.service.IOrderService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* <p>
* 服务实现类
* </p>
*/
@Service
@RequiredArgsConstructor
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {
private final ItemClient itemClient;
private final IOrderDetailService detailService;
private final CartClient cartClient;
@Override
@Transactional
public Long createOrder(OrderFormDTO orderFormDTO) {
// 1.订单数据
Order order = new Order();
// 1.1.查询商品
List<OrderDetailDTO> detailDTOS = orderFormDTO.getDetails();
// 1.2.获取商品id和数量的Map
Map<Long, Integer> itemNumMap = detailDTOS.stream()
.collect(Collectors.toMap(OrderDetailDTO::getItemId, OrderDetailDTO::getNum));
Set<Long> itemIds = itemNumMap.keySet();
// 1.3.查询商品
List<ItemDTO> items = itemClient.queryItemByIds(itemIds);
if (items == null || items.size() < itemIds.size()) {
throw new BadRequestException("商品不存在");
}
// 1.4.基于商品价格、购买数量计算商品总价:totalFee
int total = 0;
for (ItemDTO item : items) {
total += item.getPrice() * itemNumMap.get(item.getId());
}
order.setTotalFee(total);
// 1.5.其它属性
order.setPaymentType(orderFormDTO.getPaymentType());
order.setUserId(UserContext.getUser());
order.setStatus(1);
// 1.6.将Order写入数据库order表中
save(order);
// 2.保存订单详情
List<OrderDetail> details = buildDetails(order.getId(), items, itemNumMap);
detailService.saveBatch(details);
// 3.扣减库存
try {
itemClient.deductStock(detailDTOS);
} catch (Exception e) {
throw new RuntimeException("库存不足!");
}
// 4.清理购物车商品
cartClient.deleteCartItemByIds(itemIds);
return order.getId();
}
private List<OrderDetail> buildDetails(Long orderId, List<ItemDTO> items, Map<Long, Integer> numMap) {
List<OrderDetail> details = new ArrayList<>(items.size());
for (ItemDTO item : items) {
OrderDetail detail = new OrderDetail();
detail.setName(item.getName());
detail.setSpec(item.getSpec());
detail.setPrice(item.getPrice());
detail.setNum(numMap.get(item.getId()));
detail.setItemId(item.getId());
detail.setImage(item.getImage());
detail.setOrderId(orderId);
details.add(detail);
}
return details;
}
@Override
public void markOrderPaySuccess(Long orderId) {
Order order = new Order();
order.setId(orderId);
order.setStatus(2); // 假设 2 表示"已支付"状态
updateById(order);
}
}
要将service换成Client
(5)测试
这里在测试的时候遇到了2个问题
a-包导错了
因为包太多太乱了,所以有些蒙了,这里的所有的包都导成trade-service模块下的这点特别重要
b-又忘记nacos了

错误处理完毕就可以测试了
这别忘了修改

数据库别忘了连接
可以看到查询到数据库中的数据了
后台同样也有结果了
四、支付部分
1、创建pay-service模块
还是先将基础包创建出来

2、改造pay-service模块
(1)pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>hmall</artifactId>
<groupId>com.heima</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pay-service</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!--common-->
<dependency>
<groupId>com.heima</groupId>
<artifactId>hm-common</artifactId>
<version>1.0.0</version>
</dependency>
<!--api-->
<dependency>
<groupId>com.heima</groupId>
<artifactId>hm-api</artifactId>
<version>1.0.0</version>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
(2)修改模块
将相对应的包拉去过来

(3)yaml文件

(4)改造代码
扣减用户余额

新建一个接口存储用户相关的
package com.hmall.api.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient("user-service")
public interface UserClient {
@PutMapping("/users/money/deduct")
void deductMoney(@RequestParam("pw") String pw,@RequestParam("amount") Integer amount);
}
标记订单状态


package com.hmall.api.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
@FeignClient("trade-service")
public interface TradeClient {
@PutMapping("/orders/{orderId}")
void markOrderPaySuccess(@PathVariable("orderId") Long orderId);
}
改造PayOrderServiceImpl
package com.hmall.pay.service.impl;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.api.client.TradeClient;
import com.hmall.api.client.UserClient;
import com.hmall.common.exception.BizIllegalException;
import com.hmall.common.utils.BeanUtils;
import com.hmall.common.utils.UserContext;
import com.hmall.pay.domain.dto.PayApplyDTO;
import com.hmall.pay.domain.dto.PayOrderFormDTO;
import com.hmall.pay.domain.po.PayOrder;
import com.hmall.pay.enums.PayStatus;
import com.hmall.pay.mapper.PayOrderMapper;
import com.hmall.pay.service.IPayOrderService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
/**
* <p>
* 支付订单 服务实现类
* </p>
*
*/
@Service
@RequiredArgsConstructor
public class PayOrderServiceImpl extends ServiceImpl<PayOrderMapper, PayOrder> implements IPayOrderService {
private final UserClient userClient;
private final TradeClient tradeClient;
@Override
public String applyPayOrder(PayApplyDTO applyDTO) {
// 1.幂等性校验
PayOrder payOrder = checkIdempotent(applyDTO);
// 2.返回结果
return payOrder.getId().toString();
}
@Override
@Transactional
public void tryPayOrderByBalance(PayOrderFormDTO payOrderDTO) {
// 1.查询支付单
PayOrder po = getById(payOrderDTO.getId());
// 2.判断状态
if(!PayStatus.WAIT_BUYER_PAY.equalsValue(po.getStatus())){
// 订单不是未支付,状态异常
throw new BizIllegalException("交易已支付或关闭!");
}
// 3.尝试扣减余额
userClient.deductMoney(payOrderDTO.getPw(), po.getAmount());
// 4.修改支付单状态
boolean success = markPayOrderSuccess(payOrderDTO.getId(), LocalDateTime.now());
if (!success) {
throw new BizIllegalException("交易已支付或关闭!");
}
// 5.修改订单状态
tradeClient.markOrderPaySuccess(po.getBizOrderNo());
}
public boolean markPayOrderSuccess(Long id, LocalDateTime successTime) {
return lambdaUpdate()
.set(PayOrder::getStatus, PayStatus.TRADE_SUCCESS.getValue())
.set(PayOrder::getPaySuccessTime, successTime)
.eq(PayOrder::getId, id)
// 支付状态的乐观锁判断
.in(PayOrder::getStatus, PayStatus.NOT_COMMIT.getValue(), PayStatus.WAIT_BUYER_PAY.getValue())
.update();
}
private PayOrder checkIdempotent(PayApplyDTO applyDTO) {
// 1.首先查询支付单
PayOrder oldOrder = queryByBizOrderNo(applyDTO.getBizOrderNo());
// 2.判断是否存在
if (oldOrder == null) {
// 不存在支付单,说明是第一次,写入新的支付单并返回
PayOrder payOrder = buildPayOrder(applyDTO);
payOrder.setPayOrderNo(IdWorker.getId());
save(payOrder);
return payOrder;
}
// 3.旧单已经存在,判断是否支付成功
if (PayStatus.TRADE_SUCCESS.equalsValue(oldOrder.getStatus())) {
// 已经支付成功,抛出异常
throw new BizIllegalException("订单已经支付!");
}
// 4.旧单已经存在,判断是否已经关闭
if (PayStatus.TRADE_CLOSED.equalsValue(oldOrder.getStatus())) {
// 已经关闭,抛出异常
throw new BizIllegalException("订单已关闭");
}
// 5.旧单已经存在,判断支付渠道是否一致
if (!StringUtils.equals(oldOrder.getPayChannelCode(), applyDTO.getPayChannelCode())) {
// 支付渠道不一致,需要重置数据,然后重新申请支付单
PayOrder payOrder = buildPayOrder(applyDTO);
payOrder.setId(oldOrder.getId());
payOrder.setQrCodeUrl("");
updateById(payOrder);
payOrder.setPayOrderNo(oldOrder.getPayOrderNo());
return payOrder;
}
// 6.旧单已经存在,且可能是未支付或未提交,且支付渠道一致,直接返回旧数据
return oldOrder;
}
private PayOrder buildPayOrder(PayApplyDTO payApplyDTO) {
// 1.数据转换
PayOrder payOrder = BeanUtils.toBean(payApplyDTO, PayOrder.class);
// 2.初始化数据
payOrder.setPayOverTime(LocalDateTime.now().plusMinutes(120L));
payOrder.setStatus(PayStatus.WAIT_BUYER_PAY.getValue());
payOrder.setBizUserId(UserContext.getUser());
return payOrder;
}
public PayOrder queryByBizOrderNo(Long bizOrderNo) {
return lambdaQuery()
.eq(PayOrder::getBizOrderNo, bizOrderNo)
.one();
}
}
(5)测试
连接下数据库

改为local
测试成功了

后台也返回出了查询数据
结语
本文记录了微服务项目拆分实践过程,主要分为用户、交易和支付三个模块的改造。作者详细介绍了每个模块的创建步骤,包括pom文件配置、模块结构调整、yaml文件编写和代码改造。在用户模块实现了用户服务基础功能;交易模块重点处理了订单创建、库存扣减和购物车清理;支付模块则完成了余额扣减和订单状态更新。文章特别强调了包导入的正确性和Nacos服务注册的重要性,并分享了测试过程中遇到的数据库连接、包导入错误等问题的解决方法。通过逐步拆分和改造,最终完成了三个微服务模块的独立部署和测试。