- 个人主页:VON
- 文章所属专栏:微服务
微服务系列文章
重生之我在暑假学习微服务第一天《MybatisPlus-上篇》 重生之我在暑假学习微服务第二天《MybatisPlus-下篇》 重生之我在暑假学习微服务第三天《Docker-上篇》 重生之我在暑假学习微服务第四天《Docker-下篇》 重生之我在暑假学习微服务第五天《Docker部署项目篇》 重生之我在暑假学习微服务第六天《微服务之拆分项目篇》 重生之我在暑假学习微服务第七天《微服务之服务治理篇》 重生之我在暑假学习微服务第八天《OpenFeign篇》 未完待续...... 特别声明:本系列所涉及资料皆为黑马程序员课程中的资料
目录
一、前言
二、用户部分
1、先将所用到的表导进去
2、创建user-service模块
3、改造user-service模块
(1)pom文件
(2)修改模块
(3)yaml文件
(4)测试
三、交易部分
1、创建trade-service模块
2、改造trade-service模块
(1)pom文件
(2)修改模块
(3)yaml文件
(4)改造代码
(5)测试
四、支付部分
1、创建pay-service模块
2、改造pay-service模块
(1)pom文件
(2)修改模块
(3)yaml文件
(4)改造代码
(5)测试
结语
一、前言
昨天没有更新,今天将昨天的部分也补偿上去,拆分项目所用的技术我们已经掌握的差不多了,接下来开始拆分剩下的模块。
二、用户部分
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@Transactionalpublic Long createOrder(OrderFormDTO orderFormDTO) {// 1.订单数据Order order = new Order();// 1.1.查询商品List<OrderDetailDTO> detailDTOS = orderFormDTO.getDetails();// 1.2.获取商品id和数量的MapMap<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.基于商品价格、购买数量计算商品总价:totalFeeint 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;}@Overridepublic 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;@Overridepublic String applyPayOrder(PayApplyDTO applyDTO) {// 1.幂等性校验PayOrder payOrder = checkIdempotent(applyDTO);// 2.返回结果return payOrder.getId().toString();}@Override@Transactionalpublic 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服务注册的重要性,并分享了测试过程中遇到的数据库连接、包导入错误等问题的解决方法。通过逐步拆分和改造,最终完成了三个微服务模块的独立部署和测试。