ruoyi-product的ruoyi-product-dev.yml:
spring配置 spring: redis: host: localhost port: 6379 password: datasource: druid: stat-view-servlet: enabled: true loginUsername: ruoyi loginPassword: 123456 dynamic: druid: initial-size: 5 min-idle: 5 maxActive: 20 maxWait: 60000 connectTimeout: 30000 socketTimeout: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true maxPoolPreparedStatementPerConnectionSize: 20 filters: stat,slf4j connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 datasource: # 主库数据源 master: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/seata_product?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: root123 # 从库数据源 # slave: # username: # password: # url: # driver-class-name: seata: true # mybatis配置 mybatis: # 搜索指定包别名 typeAliasesPackage: com.ruoyi.product # 配置mapper的扫描,找到所有的mapper.xml映射文件 mapperLocations: classpath:mapper/**/*.xml # springdoc配置 springdoc: gatewayUrl: http://localhost:8080/${spring.application.name} api-docs: # 是否开启接口文档 enabled: falseruoyi-product示例代码:
Product.java
package com.ruoyi.product.domain; import lombok.Getter; import lombok.Setter; import java.util.Date; @Getter @Setter public class Product { private Integer id; /** * 价格 */ private Double price; /** * 库存 */ private Integer stock; private Date lastUpdateTime; }ProductMapper.java
package com.ruoyi.product.mapper; import com.ruoyi.product.domain.Product; public interface ProductMapper { public Product selectById(Long productId); public void updateById(Product product); }ProductMapper.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.ruoyi.product.mapper.ProductMapper"> <resultMap type="com.ruoyi.product.domain.Product" id="ProductResult"> <id property="id" column="id" /> <result property="price" column="price" /> <result property="stock" column="stock" /> <result property="lastUpdateTime" column="last_update_time" /> </resultMap> <select id="selectById" parameterType="com.ruoyi.product.domain.Product" resultMap="ProductResult"> select id, price, stock, last_update_time from product where id = #{productId} </select> <update id="updateById" parameterType="com.ruoyi.product.domain.Product"> update product set price = #{price}, stock = #{stock}, last_update_time = sysdate() where id = #{id} </update> </mapper>ProductService.java
package com.ruoyi.product.service; public interface ProductService { /** * 扣减库存 * * @param productId 商品 ID * @param amount 扣减数量 * @return 商品总价 */ Double reduceStock(Long productId, Integer amount); }ProductServiceImpl.java
package com.ruoyi.product.service.impl; import javax.annotation.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.baomidou.dynamic.datasource.annotation.DS; import com.ruoyi.product.domain.Product; import com.ruoyi.product.mapper.ProductMapper; import com.ruoyi.product.service.ProductService; import io.seata.core.context.RootContext; @Service public class ProductServiceImpl implements ProductService { private static final Logger log = LoggerFactory.getLogger(ProductServiceImpl.class); @Resource private ProductMapper productMapper; /** * 事务传播特性设置为 REQUIRES_NEW 开启新的事务 重要!!!!一定要使用REQUIRES_NEW * 在若依的示例中,事务的传播特性必须设置REQUIRES_NEW,但经本人测试,多服务调用事务 * 设置成默认的REQUIRED也能回滚成功 */ @Transactional @Override public Double reduceStock(Long productId, Integer amount) { log.info("=============PRODUCT START================="); log.info("当前 XID: {}", RootContext.getXID()); // 检查库存 Product product = productMapper.selectById(productId); Integer stock = product.getStock(); log.info("商品编号为 {} 的库存为{},订单商品数量为{}", productId, stock, amount); if (stock < amount) { log.warn("商品编号为{} 库存不足,当前库存:{}", productId, stock); throw new RuntimeException("库存不足"); } log.info("开始扣减商品编号为 {} 库存,单价商品价格为{}", productId, product.getPrice()); // 扣减库存 int currentStock = stock - amount; product.setStock(currentStock); productMapper.updateById(product); double totalPrice = product.getPrice() * amount; log.info("扣减商品编号为 {} 库存成功,扣减后库存为{}, {} 件商品总价为 {} ", productId, currentStock, amount, totalPrice); log.info("=============PRODUCT END================="); return totalPrice; } }ProductController.java
package com.ruoyi.product.controller; import com.ruoyi.common.core.domain.R; import com.ruoyi.product.dto.ReduceStockRequest; import com.ruoyi.product.service.ProductService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/product") public class ProductController { @Autowired private ProductService productService; @PostMapping("/reduceStock") public R<Double> reduceStock(@Validated @RequestBody ReduceStockRequest request) { Double d = productService.reduceStock(request.getProductId(), request.getAmount()); return R.ok(d); } }ReduceStockRequest.java
package com.ruoyi.product.dto; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class ReduceStockRequest { private Long productId; private Integer amount; }至此,商品服务就搭建好了,这里主要提供一个扣减商品库存的接口。
创建服务调用模块
在ruoyi-modules新建一个Module:ruoyi-call(当你也可以不新建Module,直接把Feign接口写在具调用的服务中,这里新建Module主是为了更好的体现模块之间的低耦合),该模块主要的作用是提供Feign接口,用于订单服务调用商品及账户服务,为了搭建方便,以及减少Maven依赖,pom依赖直接拷贝ruoyi-api-system中的依赖。
ruoyi-call的pom.xml:
<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"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.ruoyi</groupId> <artifactId>ruoyi-modules</artifactId> <version>3.6.6</version> </parent> <artifactId>ruoyi-call</artifactId> <packaging>jar</packaging> <name>ruoyi-call</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>com.ruoyi</groupId> <artifactId>ruoyi-common-core</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> </project>