16.5 订单/库存/账户业务微服务准备
业务需求:下订单->减库存->扣余额->改(订单)状态
16.5.1 新建订单Order-Module
- 建Module—seata-order-service2001
- POM
<dependencies> <!--nacos--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--seata--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <exclusions> <!--排除自带的版本--> <exclusion> <artifactId>seata-all</artifactId> <groupId>io.seata</groupId> </exclusion> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> </exclusion> <exclusion> <artifactId>spring-boot-starter</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions> </dependency> <!--携带上使用的版本--> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>0.9.0</version> <exclusions> <exclusion> <artifactId>fastjson</artifactId> <groupId>com.alibaba</groupId> </exclusion> </exclusions> </dependency> <!--feign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--web-actuator--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--mysql-druid--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
- YML
server: port: 2001 spring: application: name: seata-order-service cloud: alibaba: seata: #自定义事务组名称需要与seata-server中的对应 tx-service-group: fsp_tx_group nacos: discovery: server-addr: 192.168.174.128:8848 datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.174.128:3306/seata_order?useSSL=false username: root password: root feign: hystrix: enabled: false logging: level: io: seata: info mybatis: mapperLocations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true
- file.conf以及registry.conf
file.conf :从seata软件中拷贝出来,然后需要修改的如下所示
..................... service { vgroup_mapping.fsp_tx_group = "default" #修改自定义事务组名称 ##################修改IP名称为使用的 default.grouplist = "192.168.174.128:8091" enableDegrade = false disable = false max.commit.retry.timeout = "-1" max.rollback.retry.timeout = "-1" disableGlobalTransaction = false } ## transaction log store store { ## store mode: file、db #####修改模式为db mode = "db" ## file store file { dir = "sessionStore" # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions max-branch-session-size = 16384 # globe session size , if exceeded throws exceptions max-global-session-size = 512 # file buffer size , if exceeded allocate new buffer file-write-buffer-cache-size = 16384 # when recover batch read size session.reload.read_size = 100 # async, sync flush-disk-mode = async } ## database store db { ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc. datasource = "dbcp" ## mysql/oracle/h2/oceanbase etc. db-type = "mysql" #############修改数据库的相关信息 driver-class-name = "com.mysql.jdbc.Driver" url = "jdbc:mysql://192.168.174.128:3306/seata" user = "root" password = "root" min-conn = 1 max-conn = 3 global.table = "global_table" branch.table = "branch_table" lock-table = "lock_table" query-limit = 100 } } ........
registry.conf:从seata软件中拷贝出来,然后需要修改的如下所示
registry { # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa ###################### 类型修改为nacos type = "nacos" #############修改nacos的相关信息 nacos { serverAddr = "192.168.174.128:8848" namespace = "" cluster = "default" } .... } config { # file、nacos 、apollo、zk、consul、etcd3 type = "file" nacos { #############进行修改 serverAddr = "192.168.174.128" namespace = "" } .... } .....
- domain
CommonResult类
@Data @AllArgsConstructor @NoArgsConstructor public class CommonResult<T> { private Integer code; private String message; private T data; public CommonResult(Integer code, String message) { this(code,message,null); } }
Order类
@Data @AllArgsConstructor @NoArgsConstructor public class Order { private Long id; private Long userId; private Long productId; private Integer count; private BigDecimal money; /** * 订单状态:0:创建中;1:已完结 */ private Integer status; }
- Dao接口及实现
OrderDao接口
@Mapper public interface OrderDao { /** * 创建订单 */ void create(Order order); /** * 修改订单金额 */ void update(@Param("userId") Long userId, @Param("status") Integer status); }
resources文件夹下新建mapper文件夹后添加
<?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.rg.cloudalibaba.dao.OrderDao"> <!--便于后期进行扩展--> <resultMap id="BaseResultMap" type="com.rg.cloudalibaba.domain.Order"> <id column="id" property="id" jdbcType="BIGINT"/> <result column="user_id" property="userId" jdbcType="BIGINT"/> <result column="product_id" property="productId" jdbcType="BIGINT"/> <result column="count" property="count" jdbcType="INTEGER"/> <result column="money" property="money" jdbcType="DECIMAL"/> <result column="status" property="status" jdbcType="INTEGER"/> </resultMap> <insert id="create"> INSERT INTO t_order(id,user_id,product_id,`count`,money,`status`) VALUES(NULL,#{user_id},#{product_id},#{count},#{money},0); </insert> <update id="update"> UPDATE `t_order` SET `status` = 1 WHERE user_id = #{user_id} AND `status` = #{status}; </update> </mapper>
- Service接口及实现
OrderService
public interface OrderService { /** * 创建订单 */ void create(Order order); }
OrderServiceImpl
@Service @Slf4j public class OrderServiceImpl implements OrderService { /** * 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态 * 简单说: * 下订单->减库存->减余额->改状态 */ @Resource private OrderDao orderDao; @Resource private AccountService accountService; @Resource private StorageService storageService; //@GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class) public void create(Order order){ log.info("--------------->下单开始"); orderDao.create(order); //远程调用库存服务扣减库存 log.info("----------------->order-service中扣减库存开始"); storageService.decrease(order.getProductId(), order.getCount()); log.info("----------------->order-service中扣减库存结束"); //远程调用账户服务扣减余额 log.info("----------------->order-service中扣减余额开始"); accountService.decrease(order.getUserId(), order.getMoney()); log.info("----------------->order-service中扣减余额结束"); //修改订单状态 log.info("----------------->order-service中修改订单状态开始"); orderDao.update(order.getUserId(), 0); log.info("----------------->order-service中修改订单状态结束"); log.info("--------------->下单结束"); } }
StorageService
@FeignClient(value = "seata-storage-service") public interface StorageService { /** * 扣减库存 * @param productId * @param count * @return */ @PostMapping(value = "/storage/decrease") CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count); }
AccountService
@FeignClient(value = "seata-account-service") public interface AccountService { @PostMapping("/account/decrease") CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money); }
- controller
@RestController public class OrderController { @Autowired private OrderService orderService; /** * 创建订单 */ @GetMapping("/order/create") public CommonResult create( Order order) { orderService.create(order); return new CommonResult(200, "订单创建成功!"); } }
- config
DataSourceProxyConfig:自定义数据源配置类
@Configuration public class DataSourceProxyConfig { @Value("${mybatis.mapperLocations}") private String mapperLocations; @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){ return new DruidDataSource(); } @Bean public DataSourceProxy dataSourceProxy(DataSource dataSource) { return new DataSourceProxy(dataSource); } @Bean public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSourceProxy); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations)); sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory()); return sqlSessionFactoryBean.getObject(); } }
MyBatisConfig:数据源配置类
@Configuration @MapperScan({"com.rg.cloudalibaba.dao"}) public class MyBatisConfig { }
- 主启动
@EnableDiscoveryClient @EnableFeignClients @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源的自动创建 public class SeataOrderMainApp2001 { public static void main(String[] args) { SpringApplication.run(SeataOrderMainApp2001.class, args); } }
16.5.2 新建库存Storage-Module
- 建Module—seata-storage-service2002
- POM—同order2001
- YML—同order2001,只是服务name以及连接的DB需要切换下
- file.conf和registry.conf—同order2001
- domain
CommonResult—同order2001
Storage类
@Data public class Storage { private Long id; //产品id private Long productId; //总库存 private Integer total; //已用库存 private Integer used; //剩余库存 private Integer residue; }
- Dao接口及实现
StorageDao
@Mapper public interface StorageDao { /** * 扣减库存 */ void decrease(@Param("productId") Long productId, @Param("count") Integer count); }
resources文件夹下新建mapper文件夹后添加StorageMapper.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.rg.cloudalibaba.dao.StorageDao"> <resultMap id="BaseResultMap" type="com.rg.cloudalibaba.domain.Storage"> <id column="id" property="id" jdbcType="BIGINT"/> <result column="product_id" property="productId" jdbcType="BIGINT"/> <result column="total" property="total" jdbcType="INTEGER"/> <result column="used" property="used" jdbcType="INTEGER"/> <result column="residue" property="residue" jdbcType="INTEGER"/> </resultMap> <update id="decrease"> UPDATE t_storage SET used = used+#{count}, residue = residue - #{count} WHERE product_id = #{product_id}; </update> </mapper>
- Service接口及实现
StorageService类
public interface StorageService { /** * 扣减库存 */ void decrease(Long productId, Integer count); }
StorageServiceImpl类
@Service @Slf4j public class StorageServiceImpl implements StorageService { @Autowired private StorageDao storageDao; @Override public void decrease(Long productId, Integer count) { log.info("------------------------->storage-service中扣减库存开始"); storageDao.decrease(productId,count); log.info("------------------------->storage-service中扣减库存结束"); } }
- controller
@RestController public class StorageController { @Autowired private StorageService storageService; @RequestMapping("/storage/decrease") public CommonResult decrease(Long productId,Integer count){ storageService.decrease(productId,count); return new CommonResult(200, "扣减库存成功!"); } }
- Config—同order2001
- 主启动
@EnableDiscoveryClient @EnableFeignClients @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源的自动创建 public class SeataStorageMainApp2002 { public static void main(String[] args) { SpringApplication.run(SeataStorageMainApp2002.class, args); } }
16.5.3 新建账户Account-Module
- 建Module—seata-account-service2003
- POM—同order2001
- YML—同order2001,只是服务name以及连接的DB需要切换下
- file.conf和registry.conf—同order2001
- domain
CommonResult—同order2001
Account类
@Data @AllArgsConstructor @NoArgsConstructor public class Account { private Long id; //用户id private Long userId; //总额度 private BigDecimal total; //已用额度 private BigDecimal used; //剩余额度 private BigDecimal residue; }
- Dao接口及实现
AccountDao
@Mapper public interface AccountDao { /** * 扣减账户余额 */ void decrease(@Param("userId") Long userId, @Param("money") BigDecimal money); }
resources文件夹下新建mapper文件夹后添加AccountMapper.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.rg.cloudalibaba.dao.AccountDao"> <update id="decrease"> UPDATE t_account SET residue = residue - #{money}, used = used+#{money} WHERE user_id = #{user_id}; </update> </mapper>
Service接口及实现
AccountService
public interface AccountService { /** * 扣减账户余额 * @param userId 用户id * @param money 金额 */ void decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money); }
AccountServiceImpl
@Service @Slf4j public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; @Override public void decrease(Long userId, BigDecimal money) { log.info("-------------------account-service扣减账号余额开始"); accountDao.decrease(userId,money); log.info("-------------------account-service扣减账号余额结束"); } }
- controller
@RestController public class AccountController { @Autowired private AccountService accountService; @RequestMapping("/account/decrease") CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money){ accountService.decrease(userId,money); return new CommonResult(200, "扣减账号余额成功!"); } }
- Config配置—同order2001
- 主启动
@EnableDiscoveryClient @EnableFeignClients @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源的自动创建 public class SeataAccountMainApp2003 { public static void main(String[] args) { SpringApplication.run(SeataAccountMainApp2003.class, args); } }