[Java]微服务拆分2

用户服务拆分

创建数据库: 执行资料中sql文件即可

在hmall下新建一个module,命名为user-service

导入用户模块需要使用的包

 <dependencies>
        <!--common-->
        <dependency>
            <groupId>com.heima</groupId>
            <artifactId>hm-common</artifactId>
            <version>1.0.0</version>
        </dependency>
        <!--hm-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>

添加启动类和配置文件

package com.hmall.user;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@MapperScan("com.hmall.user.mapper")
@EnableFeignClients(basePackages = "com.hmall.api.client")
@SpringBootApplication
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class, args);
    }
}

server:
  port: 8084 #每个微服务运行在不同端口
spring:
  application:
    name: user-service #每个微服务对应一个名称
  cloud:
    nacos:
      server-addr: 192.168.1.97:8848 #nacos地址
  profiles:
    active: dev #读取dev的配置
  datasource: #数据库配置
    url: jdbc:mysql://${hm.db.host}:3306/hm-user?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: ${hm.db.pw}
mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
  global-config:
    db-config:
      update-strategy: not_null
      id-type: auto
logging: #日志配置
  level: #日志记录级别
    com.hmall: debug
  pattern: #日志日期格式
    dateformat: HH:mm:ss:SSS
  file:  #日志保存目录
    path: "logs/${spring.application.name}"
knife4j: #swagger配置
  enable: true
  openapi:
    title: 黑马商城用户管理接口文档
    description: "黑马商城用户管理接口文档"
    email: 123456@qq.com
    concat: 王
    url: https://www.itcast.cn
    version: v1.0.0
    group:
      default:
        group-name: default
        api-rule: package
        api-rule-resources: # 指定扫描的包
          - com.hmall.user.controller

登录注册代码迁移

  1. 项目中登录注册采用的是JWT模式
  2. hmall.jsk是JWT加密的密钥文件
  3. JwtProperties文件是读取密钥时用于封装密钥信息
  4. SecurityConfig文件封装了加密用户密码的方法 和 读取密钥的方法
  5. JwtTool文件用于生成Token和解析Token
  1. 在配置文件中配置密钥文件的位置信息

    server:
    ... ...
    hm:
    jwt:
    location: classpath:hmall.jks
    alias: hmall
    password: hmall123
    tokenTTL: 30m

业务代码迁移

  1. 迁移工具类
  1. 迁移mapper
  • 由于项目中存在多个同名类, 所以需要手动导入类文件
  1. 迁移service
  1. 迁移controller

配置启动项, 启动服务

交易服务拆分

在hmall下新建一个module,命名为trade-service

导入订单模块需要使用的包

 <dependencies>
        <!--common-->
        <dependency>
            <groupId>com.heima</groupId>
            <artifactId>hm-common</artifactId>
            <version>1.0.0</version>
        </dependency>
        <!--hm-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>

添加启动类和配置文件

package com.hmall.trade;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@MapperScan("com.hmall.trade.mapper")
@EnableFeignClients(basePackages = "com.hmall.api.client")
@SpringBootApplication
public class TradeApplication {
    public static void main(String[] args) {
        SpringApplication.run(TradeApplication.class, args);
    }
}

server:
  port: 8085 #每个微服务运行在不同端口
spring:
  application:
    name: trade-service #每个微服务对应一个名称
  cloud:
    nacos:
      server-addr: 192.168.1.97:8848 #nacos地址
  profiles:
    active: dev #读取dev的配置
  datasource: #数据库配置
    url: jdbc:mysql://${hm.db.host}:3306/hm-trade?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: ${hm.db.pw}
mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
  global-config:
    db-config:
      update-strategy: not_null
      id-type: auto
logging: #日志配置
  level: #日志记录级别
    com.hmall: debug
  pattern: #日志日期格式
    dateformat: HH:mm:ss:SSS
  file:  #日志保存目录
    path: "logs/${spring.application.name}"
knife4j: #swagger配置
  enable: true
  openapi:
    title: 黑马商城交易管理接口文档
    description: "黑马商城交易管理接口文档"
    email: 123456@qq.com
    concat: 王
    url: https://www.itcast.cn
    version: v1.0.0
    group:
      default:
        group-name: default
        api-rule: package
        api-rule-resources: # 指定扫描的包
          - com.hmall.trade.controller

业务代码迁移

  1. 迁移工具类
  1. 迁移mapper
  1. 迁移service
  1. 迁移controller

解决OrderServiceImpl报错

  1. 在交易服务中,用户下单时需要做下列事情:
  • 根据id查询商品列表
  • 计算商品总价
  • 保存订单
  • 扣减库存
  • 清理购物车商品
  1. 标黑的业务需要远程调用其他服务
  • 查询商品、扣减库存都是与商品有关的业务,在item-service中有相关功能;
  • 清理购物车商品是购物车业务,在cart-service中有相关功能。
  1. 抽取ItemClient接口

    @FeignClient(value = "item-service",configuration = DefaultFeignConfig.class) // 指定拉取的服务名称
    public interface ItemClient {
    @PutMapping("/items/stock/deduct")
    void deductStock(@RequestBody List<OrderDetailDTO> items);
    }

  • hm-api中导入OrderDetailDTO实体类
  • trade-service中删除OrderDetailDTO实体类
  • trade-service中的OrderFormDTO重新导包
  1. 抽取CartClient接口

    @FeignClient(value = "cart-service",configuration = DefaultFeignConfig.class) // 指定拉取的服务名称
    public interface CartClient {

     @DeleteMapping("/carts")
     void deleteCartItemByIds(@RequestParam("ids") Collection<Long> ids);
    

    }

  2. 改造OrderServiceImpl

    @Service
    @RequiredArgsConstructor
    public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

     private final IOrderDetailService detailService;
     private final ItemClient itemClient;
     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.清理购物车商品
         // 改为远程调用
         cartClient.deleteCartItemByIds(itemIds);
    
         // 4.扣减库存
         try {
             // 改为远程调用
             itemClient.deductStock(detailDTOS);
         } catch (Exception e) {
             throw new RuntimeException("库存不足!");
         }
         return order.getId();
     }
    

    }

  3. 清除重复DTO, 引入hm-api模块, 重新导包

配置启动项, 启动服务, 测试id: 1654779387523936258

支付服务拆分

新建UserCllient客户端 和 TradeClient客户端

@FeignClient("user-service")
public interface UserClient {

    @PutMapping("/users/money/deduct")
    void deductMoney(@RequestParam("pw") String pw, @RequestParam("amount") Integer amount);
}

@FeignClient("trade-service")
public interface TradeClient {

    @PutMapping("/orders/{orderId}")
    void markOrderPaySuccess(@PathVariable("orderId") Long orderId);
}

在hmall下新建一个module,命名为pay-service

导入订单模块需要使用的包

 <dependencies>
        <!--common-->
        <dependency>
            <groupId>com.heima</groupId>
            <artifactId>hm-common</artifactId>
            <version>1.0.0</version>
        </dependency>
        <!--hm-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>

添加启动类和配置文件

package com.hmall.pay;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@MapperScan("com.hmall.pay.mapper")
@EnableFeignClients(basePackages = "com.hmall.api.client")
@SpringBootApplication
public class PayApplication {
    public static void main(String[] args) {
        SpringApplication.run(PayApplication.class, args);
    }
}

server:
  port: 8086 #每个微服务运行在不同端口
spring:
  application:
    name: pay-service #每个微服务对应一个名称
  cloud:
    nacos:
      server-addr: 192.168.1.97:8848 #nacos地址
  profiles:
    active: dev #读取dev的配置
  datasource: #数据库配置
    url: jdbc:mysql://${hm.db.host}:3306/hm-pay?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: ${hm.db.pw}
mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
  global-config:
    db-config:
      update-strategy: not_null
      id-type: auto
logging: #日志配置
  level: #日志记录级别
    com.hmall: debug
  pattern: #日志日期格式
    dateformat: HH:mm:ss:SSS
  file:  #日志保存目录
    path: "logs/${spring.application.name}"
knife4j: #swagger配置
  enable: true
  openapi:
    title: 黑马商城支付管理接口文档
    description: "黑马商城支付管理接口文档"
    email: 123456@qq.com
    concat: 王
    url: https://www.itcast.cn
    version: v1.0.0
    group:
      default:
        group-name: default
        api-rule: package
        api-rule-resources: # 指定扫描的包
          - com.hmall.pay.controller

业务代码迁移

  1. 迁移工具类
  1. 迁移mapper
  1. 迁移service
  1. 迁移枚举
  1. 迁移controller

解决PayOrderServiceImpl报错

@Service
@RequiredArgsConstructor
public class PayOrderServiceImpl extends ServiceImpl<PayOrderMapper, PayOrder> implements IPayOrderService {

    private final UserClient userClient;
    private final TradeClient tradeClient;

    ... ...
    
    @Override
    @Transactional
    public void tryPayOrderByBalance(PayOrderFormDTO payOrderFormDTO) {
        // 1.查询支付单
        PayOrder po = getById(payOrderFormDTO.getId());
        // 2.判断状态
        if(!PayStatus.WAIT_BUYER_PAY.equalsValue(po.getStatus())){
            // 订单不是未支付,状态异常
            throw new BizIllegalException("交易已支付或关闭!");
        }
        // 3.尝试扣减余额
        // 远程调用
        userClient.deductMoney(payOrderFormDTO.getPw(), po.getAmount());
        // 4.修改支付单状态
        boolean success = markPayOrderSuccess(payOrderFormDTO.getId(), LocalDateTime.now());
        if (!success) {
            throw new BizIllegalException("交易已支付或关闭!");
        }
        // 5.修改订单状态
        // 远程调用
        tradeClient.markOrderPaySuccess(po.getBizOrderNo());
    }

    ... ...
}

在支付服务的PayController中添加一个接口方便测试

    @ApiOperation("查询支付单")
    @GetMapping
    public List<PayOrderVO> queryPayOrders(){
        return BeanUtils.copyList(payOrderService.list(), PayOrderVO.class);
    }

配置启动项, 启动服务

相关推荐
Yvemil73 分钟前
《开启微服务之旅:Spring Boot 从入门到实践》(三)
java
Anna。。4 分钟前
Java入门2-idea 第五章:IO流(java.io包中)
java·开发语言·intellij-idea
.生产的驴26 分钟前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
爱上语文28 分钟前
宠物管理系统:Dao层
java·开发语言·宠物
王ASC1 小时前
SpringMVC的URL组成,以及URI中对/斜杠的处理,解决IllegalStateException: Ambiguous mapping
java·mvc·springboot·web
是小崔啊1 小时前
开源轮子 - Apache Common
java·开源·apache
因我你好久不见1 小时前
springboot java ffmpeg 视频压缩、提取视频帧图片、获取视频分辨率
java·spring boot·ffmpeg
程序员shen1616111 小时前
抖音短视频saas矩阵源码系统开发所需掌握的技术
java·前端·数据库·python·算法
Ling_suu2 小时前
SpringBoot3——Web开发
java·服务器·前端
维李设论2 小时前
Node.js的Web服务在Nacos中的实践
前端·spring cloud·微服务·eureka·nacos·node.js·express