关注JEECG发展历程 关注最新动态和版本, 记录JEECG成长点滴 更新日志 - 技术支持 - 招聘英才

JEECG最新版本下载 JEECG智能开发平台 - 显著提高开发效率 常见问题 - 入门视频 - 参与开源团队

商务QQ: 69893005、418799587 商务热线(5*8小时): 010-64808099 官方邮箱: jeecgos@163.com

查看: 3192|回复: 0

jeecgboot集成seata实战

[复制链接]
发表于 2022-4-24 00:04:05 | 显示全部楼层 |阅读模式
1. 环境描述

JeecgBoot 3.0

seata版本 : 1.3.0



2.数据库搭建

先创建3个数据库,加上jeecg-boot自有的数据库,一共4个数据库

首先在四个数据库中引入undo_log表

  1. CREATE TABLE `undo_log` (
  2.   `id` bigint(20) NOT NULL AUTO_INCREMENT,
  3.   `branch_id` bigint(20) NOT NULL,
  4.   `xid` varchar(100) NOT NULL,
  5.   `context` varchar(128) NOT NULL,
  6.   `rollback_info` longblob NOT NULL,
  7.   `log_status` int(11) NOT NULL,
  8.   `log_created` datetime NOT NULL,
  9.   `log_modified` datetime NOT NULL,
  10.   PRIMARY KEY (`id`),
  11.   UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
  12. ) ENGINE=InnoDB AUTO_INCREMENT=137 DEFAULT CHARSET=utf8;
复制代码

在jeecg-account中,创建表并插入数据

  1. CREATE TABLE `account` (
  2.   `id` int(11) NOT NULL AUTO_INCREMENT,
  3.   `user_id` int(11) DEFAULT NULL,
  4.   `balance` int(11) DEFAULT NULL,
  5.   `update_time` datetime DEFAULT NULL,
  6.   PRIMARY KEY (`id`)
  7. ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;

  8. INSERT INTO `account` (`id`, `user_id`, `balance`, `update_time`) VALUES ('1', '1', '200', '2021-01-15 00:02:17');
复制代码

在jeecg-order库中,创建表

  1. CREATE TABLE `orders` (
  2.   `id` int(11) NOT NULL AUTO_INCREMENT,
  3.   `user_id` int(11) DEFAULT NULL,
  4.   `product_id` int(11) DEFAULT NULL,
  5.   `pay_amount` int(11) DEFAULT NULL,
  6.   `add_time` datetime DEFAULT NULL,
  7.   `update_time` datetime DEFAULT NULL,
  8.   PRIMARY KEY (`id`)
  9. ) ENGINE=InnoDB AUTO_INCREMENT=50 DEFAULT CHARSET=utf8mb4;
复制代码

在jeecg-product中,创建表并插入数据

  1. CREATE TABLE `product` (
  2.   `id` int(11) NOT NULL AUTO_INCREMENT,
  3.   `name` varchar(255) DEFAULT NULL,
  4.   `price` int(11) DEFAULT NULL,
  5.   `stock` int(11) DEFAULT NULL,
  6.   `add_time` datetime DEFAULT NULL,
  7.   `update_time` datetime DEFAULT NULL,
  8.   PRIMARY KEY (`id`)
  9. ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;

  10. INSERT INTO `product` (`id`, `name`, `price`, `stock`, `add_time`, `update_time`) VALUES ('1', '电池', '10', '67', '2021-01-15 00:00:32', '2021-01-15 00:00:35');
复制代码

3. 坐标引入<!-- seata-spring-boot-starter -->
  1. <dependency>
  2. <groupId>io.seata</groupId>
  3. <artifactId>seata-spring-boot-starter</artifactId>
  4. <version>1.3.0</version>
  5. </dependency>
复制代码

4. yml配置文件seata:
  1. config:
  2.     type: file
  3.   application-id: springboot-seata
  4.   #  enable-auto-data-source-proxy: false
  5.   registry:
  6.     type: file
  7.   service:
  8.     grouplist:
  9.       default: 127.0.0.1:8091
  10.     vgroup-mapping:
  11.       springboot-seata-group: default
  12.   # seata 事务组编号 用于TC集群名
  13.   tx-service-group: springboot-seata-group
  14. spring:
  15.   datasource:
  16.     dynamic:
  17.       datasource:
  18.         master:
  19.           url: jdbc:mysql://127.0.0.1:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
  20.           username: root
  21.           password: root
  22.           driver-class-name: com.mysql.cj.jdbc.Driver
  23.         # 设置 账号数据源配置
  24.         account-ds:
  25.           driver-class-name: com.mysql.cj.jdbc.Driver
  26.           password: root
  27.           url: jdbc:mysql://127.0.0.1:3306/jeecg-account?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
  28.           username: root
  29.           # 设置 订单数据源配置
  30.         order-ds:
  31.           driver-class-name: com.mysql.cj.jdbc.Driver
  32.           password: root
  33.           url: jdbc:mysql://127.0.0.1:3306/jeecg-order?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
  34.           username: root
  35.           # 设置商品 数据源配置
  36.         product-ds:
  37.           driver-class-name: com.mysql.cj.jdbc.Driver
  38.           password: root
  39.           url: jdbc:mysql://127.0.0.1:3306/jeecg-product?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
  40.           username: root
  41.          
  42.       # 设置默认数据源或者数据源组 默认值即为master
  43.       primary: master   # 默认指定一个数据源
  44.       # 开启对 seata的支持
  45.       seata: true
复制代码

5. Seata启动

采用jeecg-boot单体模式测试,使用默认的文件进行seata配置,不需要做额外的配置,直接启动seata-server.bat即可。


6. 代码编写

项目结构


其中三个实体类对应如下

  1. package org.jeecg.modules.seata.entity;

  2. import lombok.Data;

  3. import java.math.BigDecimal;
  4. import java.util.Date;

  5. @Data
  6. public class Orders {

  7.     private Integer id;

  8.     private Integer userId;

  9.     private Integer productId;

  10.     private BigDecimal payAmount;

  11.     private Date addTime;

  12.     private Date updateTime;

  13. }
复制代码
  1. package org.jeecg.modules.seata.entity;

  2. import lombok.Data;

  3. import java.math.BigDecimal;
  4. import java.util.Date;

  5. @Data
  6. public class Product {

  7.     private Integer id;

  8.     private String name;

  9.     private BigDecimal price;

  10.     private Integer stock;

  11.     private Date addTime;

  12.     private Date updateTime;
  13. }
复制代码
  1. package org.jeecg.modules.seata.entity;

  2. import lombok.Data;

  3. import java.math.BigDecimal;
  4. import java.util.Date;

  5. @Data
  6. public class Account {

  7.     private Integer id;

  8.     private Integer userId;

  9.     private BigDecimal balance;

  10.     private Date updateTime;
  11. }
复制代码

Mapper对应代码如下

  1. package org.jeecg.modules.seata.mapper;

  2. import org.apache.ibatis.annotations.Mapper;
  3. import org.apache.ibatis.annotations.Param;
  4. import org.jeecg.modules.seata.entity.Product;

  5. @Mapper
  6. public interface ProductMapper {

  7.     int deleteByPrimaryKey(Integer id);

  8.     int insert(Product record);

  9.     int insertSelective(Product record);

  10.     Product selectByPrimaryKey(Integer id);

  11.     int updateByPrimaryKeySelective(Product record);

  12.     int updateByPrimaryKey(Product record);

  13.     int reduceStock(@Param("productId") Integer productId, @Param("amount") Integer amount);
  14. }
复制代码
  1. package org.jeecg.modules.seata.mapper;

  2. import org.apache.ibatis.annotations.Mapper;
  3. import org.jeecg.modules.seata.entity.Orders;

  4. @Mapper
  5. public interface OrdersMapper {

  6.     int deleteByPrimaryKey(Integer id);

  7.     int insert(Orders record);

  8.     int insertSelective(Orders record);

  9.     Orders selectByPrimaryKey(Integer id);

  10.     int updateByPrimaryKeySelective(Orders record);

  11.     int updateByPrimaryKey(Orders record);
  12. }
复制代码
  1. package org.jeecg.modules.seata.mapper;

  2. import org.apache.ibatis.annotations.Mapper;
  3. import org.apache.ibatis.annotations.Param;
  4. import org.jeecg.modules.seata.entity.Account;

  5. import java.math.BigDecimal;

  6. @Mapper
  7. public interface AccountMapper {

  8.     int deleteByPrimaryKey(Integer id);

  9.     int insert(Account record);

  10.     int insertSelective(Account record);

  11.     Account selectByPrimaryKey(Integer id);

  12.     Account selectAccountByUserId(Integer userId);

  13.     int updateByPrimaryKeySelective(Account record);

  14.     int updateByPrimaryKey(Account record);

  15.     int reduceBalance(@Param("userId") Integer userId, @Param("money") BigDecimal money);
  16. }
复制代码
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="org.jeecg.modules.seata.mapper.ProductMapper">
  4.     <resultMap id="BaseResultMap" type="org.jeecg.modules.seata.entity.Product">
  5.         <id column="id" jdbcType="INTEGER" property="id"/>
  6.         <result column="name" jdbcType="VARCHAR" property="name"/>
  7.         <result column="price" jdbcType="DECIMAL" property="price"/>
  8.         <result column="stock" jdbcType="INTEGER" property="stock"/>
  9.         <result column="add_time" jdbcType="TIMESTAMP" property="addTime"/>
  10.         <result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
  11.     </resultMap>
  12.     <sql id="Base_Column_List">
  13.     id, name, price, stock, add_time, update_time
  14.   </sql>
  15.     <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
  16.         select
  17.         <include refid="Base_Column_List"/>
  18.         from product
  19.         where id = #{id,jdbcType=INTEGER}
  20.     </select>
  21.     <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
  22.     delete from product
  23.     where id = #{id,jdbcType=INTEGER}
  24.   </delete>
  25.     <insert id="insert" parameterType="org.jeecg.modules.seata.entity.Product">
  26.     insert into product (id, name, price,
  27.       stock, add_time, update_time
  28.       )
  29.     values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{price,jdbcType=DECIMAL},
  30.       #{stock,jdbcType=INTEGER}, #{addTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP}
  31.       )
  32.   </insert>
  33.     <insert id="insertSelective" parameterType="org.jeecg.modules.seata.entity.Product">
  34.         insert into product
  35.         <trim prefix="(" suffix=")" suffixOverrides=",">
  36.             <if test="id != null">
  37.                 id,
  38.             </if>
  39.             <if test="name != null">
  40.                 name,
  41.             </if>
  42.             <if test="price != null">
  43.                 price,
  44.             </if>
  45.             <if test="stock != null">
  46.                 stock,
  47.             </if>
  48.             <if test="addTime != null">
  49.                 add_time,
  50.             </if>
  51.             <if test="updateTime != null">
  52.                 update_time,
  53.             </if>
  54.         </trim>
  55.         <trim prefix="values (" suffix=")" suffixOverrides=",">
  56.             <if test="id != null">
  57.                 #{id,jdbcType=INTEGER},
  58.             </if>
  59.             <if test="name != null">
  60.                 #{name,jdbcType=VARCHAR},
  61.             </if>
  62.             <if test="price != null">
  63.                 #{price,jdbcType=DECIMAL},
  64.             </if>
  65.             <if test="stock != null">
  66.                 #{stock,jdbcType=INTEGER},
  67.             </if>
  68.             <if test="addTime != null">
  69.                 #{addTime,jdbcType=TIMESTAMP},
  70.             </if>
  71.             <if test="updateTime != null">
  72.                 #{updateTime,jdbcType=TIMESTAMP},
  73.             </if>
  74.         </trim>
  75.     </insert>
  76.     <update id="updateByPrimaryKeySelective" parameterType="org.jeecg.modules.seata.entity.Product">
  77.         update product
  78.         <set>
  79.             <if test="name != null">
  80.                 name = #{name,jdbcType=VARCHAR},
  81.             </if>
  82.             <if test="price != null">
  83.                 price = #{price,jdbcType=DECIMAL},
  84.             </if>
  85.             <if test="stock != null">
  86.                 stock = #{stock,jdbcType=INTEGER},
  87.             </if>
  88.             <if test="addTime != null">
  89.                 add_time = #{addTime,jdbcType=TIMESTAMP},
  90.             </if>
  91.             <if test="updateTime != null">
  92.                 update_time = #{updateTime,jdbcType=TIMESTAMP},
  93.             </if>
  94.         </set>
  95.         where id = #{id,jdbcType=INTEGER}
  96.     </update>
  97.     <update id="updateByPrimaryKey" parameterType="org.jeecg.modules.seata.entity.Product">
  98.     update product
  99.     set name = #{name,jdbcType=VARCHAR},
  100.       price = #{price,jdbcType=DECIMAL},
  101.       stock = #{stock,jdbcType=INTEGER},
  102.       add_time = #{addTime,jdbcType=TIMESTAMP},
  103.       update_time = #{updateTime,jdbcType=TIMESTAMP}
  104.     where id = #{id,jdbcType=INTEGER}
  105.   </update>

  106.     <!--减库存-->
  107.     <update id="reduceStock">
  108.     update product SET stock = stock - #{amount, jdbcType=INTEGER}
  109.     WHERE id = #{productId, jdbcType=INTEGER} AND stock >= #{amount, jdbcType=INTEGER}
  110.   </update>

  111. </mapper>
复制代码
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="org.jeecg.modules.seata.mapper.OrdersMapper">
  4.   <resultMap id="BaseResultMap" type="org.jeecg.modules.seata.entity.Orders">
  5.     <id column="id" jdbcType="INTEGER" property="id" />
  6.     <result column="user_id" jdbcType="INTEGER" property="userId" />
  7.     <result column="product_id" jdbcType="INTEGER" property="productId" />
  8.     <result column="pay_amount" jdbcType="DECIMAL" property="payAmount" />
  9.     <result column="add_time" jdbcType="TIMESTAMP" property="addTime" />
  10.     <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
  11.   </resultMap>
  12.   <sql id="Base_Column_List">
  13.     id, user_id, product_id, pay_amount, add_time, update_time
  14.   </sql>
  15.   <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
  16.     select
  17.     <include refid="Base_Column_List" />
  18.     from orders
  19.     where id = #{id,jdbcType=INTEGER}
  20.   </select>
  21.   <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
  22.     delete from orders
  23.     where id = #{id,jdbcType=INTEGER}
  24.   </delete>
  25.   <insert id="insert" parameterType="org.jeecg.modules.seata.entity.Orders">
  26.     insert into orders (id, user_id, product_id,
  27.       pay_amount, add_time, update_time
  28.       )
  29.     values (#{id,jdbcType=INTEGER}, #{userId,jdbcType=INTEGER}, #{productId,jdbcType=INTEGER},
  30.       #{payAmount,jdbcType=DECIMAL}, #{addTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP}
  31.       )
  32.   </insert>

  33.   <insert id="insertSelective" parameterType="org.jeecg.modules.seata.entity.Orders">
  34.     insert into orders
  35.     <trim prefix="(" suffix=")" suffixOverrides=",">
  36.       <if test="id != null">
  37.         id,
  38.       </if>
  39.       <if test="userId != null">
  40.         user_id,
  41.       </if>
  42.       <if test="productId != null">
  43.         product_id,
  44.       </if>
  45.       <if test="payAmount != null">
  46.         pay_amount,
  47.       </if>
  48.       <if test="addTime != null">
  49.         add_time,
  50.       </if>
  51.       <if test="updateTime != null">
  52.         update_time,
  53.       </if>
  54.     </trim>
  55.     <trim prefix="values (" suffix=")" suffixOverrides=",">
  56.       <if test="id != null">
  57.         #{id,jdbcType=INTEGER},
  58.       </if>
  59.       <if test="userId != null">
  60.         #{userId,jdbcType=INTEGER},
  61.       </if>
  62.       <if test="productId != null">
  63.         #{productId,jdbcType=INTEGER},
  64.       </if>
  65.       <if test="payAmount != null">
  66.         #{payAmount,jdbcType=DECIMAL},
  67.       </if>
  68.       <if test="addTime != null">
  69.         #{addTime,jdbcType=TIMESTAMP},
  70.       </if>
  71.       <if test="updateTime != null">
  72.         #{updateTime,jdbcType=TIMESTAMP},
  73.       </if>
  74.     </trim>
  75.   </insert>

  76.   <update id="updateByPrimaryKeySelective" parameterType="org.jeecg.modules.seata.entity.Orders">
  77.     update orders
  78.     <set>
  79.       <if test="userId != null">
  80.         user_id = #{userId,jdbcType=INTEGER},
  81.       </if>
  82.       <if test="productId != null">
  83.         product_id = #{productId,jdbcType=INTEGER},
  84.       </if>
  85.       <if test="payAmount != null">
  86.         pay_amount = #{payAmount,jdbcType=DECIMAL},
  87.       </if>
  88.       <if test="addTime != null">
  89.         add_time = #{addTime,jdbcType=TIMESTAMP},
  90.       </if>
  91.       <if test="updateTime != null">
  92.         update_time = #{updateTime,jdbcType=TIMESTAMP},
  93.       </if>
  94.     </set>
  95.     where id = #{id,jdbcType=INTEGER}
  96.   </update>
  97.   <update id="updateByPrimaryKey" parameterType="org.jeecg.modules.seata.entity.Orders">
  98.     update orders
  99.     set user_id = #{userId,jdbcType=INTEGER},
  100.       product_id = #{productId,jdbcType=INTEGER},
  101.       pay_amount = #{payAmount,jdbcType=DECIMAL},
  102.       add_time = #{addTime,jdbcType=TIMESTAMP},
  103.       update_time = #{updateTime,jdbcType=TIMESTAMP}
  104.     where id = #{id,jdbcType=INTEGER}
  105.   </update>
  106. </mapper>
复制代码
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="org.jeecg.modules.seata.mapper.AccountMapper">

  4.     <resultMap id="BaseResultMap" type="org.jeecg.modules.seata.entity.Account">
  5.         <id column="id" jdbcType="INTEGER" property="id"/>
  6.         <result column="user_id" jdbcType="INTEGER" property="userId"/>
  7.         <result column="balance" jdbcType="DECIMAL" property="balance"/>
  8.         <result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
  9.     </resultMap>

  10.     <sql id="Base_Column_List">
  11.     id, user_id, balance, update_time
  12.   </sql>

  13.     <!--根据userId-->
  14.     <select id="selectAccountByUserId" parameterType="java.lang.Integer" resultMap="BaseResultMap">
  15.         select
  16.         <include refid="Base_Column_List"/>
  17.         from account
  18.         where user_id = #{userId, jdbcType=INTEGER}
  19.     </select>

  20.     <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
  21.         select
  22.         <include refid="Base_Column_List"/>
  23.         from account
  24.         where id = #{id,jdbcType=INTEGER}
  25.     </select>

  26.     <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
  27.     delete from account
  28.     where id = #{id,jdbcType=INTEGER}
  29.   </delete>

  30.     <insert id="insert" parameterType="org.jeecg.modules.seata.entity.Account">
  31.     insert into account (id, user_id, balance,
  32.       update_time)
  33.     values (#{id,jdbcType=INTEGER}, #{userId,jdbcType=INTEGER}, #{balance,jdbcType=DOUBLE},
  34.       #{updateTime,jdbcType=TIMESTAMP})
  35.   </insert>

  36.     <insert id="insertSelective" parameterType="org.jeecg.modules.seata.entity.Account">
  37.         insert into account
  38.         <trim prefix="(" suffix=")" suffixOverrides=",">
  39.             <if test="id != null">
  40.                 id,
  41.             </if>
  42.             <if test="userId != null">
  43.                 user_id,
  44.             </if>
  45.             <if test="balance != null">
  46.                 balance,
  47.             </if>
  48.             <if test="updateTime != null">
  49.                 update_time,
  50.             </if>
  51.         </trim>
  52.         <trim prefix="values (" suffix=")" suffixOverrides=",">
  53.             <if test="id != null">
  54.                 #{id,jdbcType=INTEGER},
  55.             </if>
  56.             <if test="userId != null">
  57.                 #{userId,jdbcType=INTEGER},
  58.             </if>
  59.             <if test="balance != null">
  60.                 #{balance,jdbcType=DOUBLE},
  61.             </if>
  62.             <if test="updateTime != null">
  63.                 #{updateTime,jdbcType=TIMESTAMP},
  64.             </if>
  65.         </trim>
  66.     </insert>

  67.     <update id="updateByPrimaryKeySelective" parameterType="org.jeecg.modules.seata.entity.Account">
  68.         update account
  69.         <set>
  70.             <if test="userId != null">
  71.                 user_id = #{userId,jdbcType=INTEGER},
  72.             </if>
  73.             <if test="balance != null">
  74.                 balance = #{balance,jdbcType=DOUBLE},
  75.             </if>
  76.             <if test="updateTime != null">
  77.                 update_time = #{updateTime,jdbcType=TIMESTAMP},
  78.             </if>
  79.         </set>
  80.         where id = #{id,jdbcType=INTEGER}
  81.     </update>

  82.    <update id="updateByPrimaryKey" parameterType="org.jeecg.modules.seata.entity.Account">
  83.     update account
  84.     set user_id = #{userId,jdbcType=INTEGER},
  85.       balance = #{balance,jdbcType=DOUBLE},
  86.       update_time = #{updateTime,jdbcType=TIMESTAMP}
  87.     where id = #{id,jdbcType=INTEGER}
  88.   </update>

  89.     <!--减余额-->
  90.     <update id="reduceBalance">
  91.     update account
  92.         SET balance = balance - #{money}
  93.     WHERE user_id = #{userId, jdbcType=INTEGER}
  94.         AND balance >= ${money}
  95.   </update>
  96. </mapper>
复制代码

Service对应的代码如下

  1. package org.jeecg.modules.seata.service;

  2. import org.jeecg.modules.seata.entity.Product;

  3. public interface ProductService {

  4.     /**
  5.      * 减库存
  6.      *
  7.      * @param productId 商品 ID
  8.      * @param amount    扣减数量
  9.      * @throws Exception 扣减失败时抛出异常
  10.      */
  11.     Product reduceStock(Integer productId, Integer amount) throws Exception;

  12. }
复制代码
  1. package org.jeecg.modules.seata.service;

  2. public interface OrderService {

  3.     /**
  4.      * 下订单
  5.      *
  6.      * @param userId 用户id
  7.      * @param productId 产品id
  8.      * @return 订单id
  9.      * @throws Exception 创建订单失败,抛出异常
  10.      */
  11.     Integer createOrder(Integer userId, Integer productId) throws Exception;

  12. }
复制代码
  1. package org.jeecg.modules.seata.service;

  2. import java.math.BigDecimal;

  3. public interface AccountService {

  4.     /**
  5.      * 减余额
  6.      *
  7.      * @param userId 用户id
  8.      * @param money  扣减金额
  9.      * @throws Exception 失败时抛出异常
  10.      */
  11.     void reduceBalance(Integer userId, BigDecimal money) throws Exception;

  12. }
复制代码
  1. package org.jeecg.modules.seata.service.impl;

  2. import com.baomidou.dynamic.datasource.annotation.DS;
  3. import io.seata.core.context.RootContext;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.jeecg.modules.seata.entity.Product;
  6. import org.jeecg.modules.seata.mapper.ProductMapper;
  7. import org.jeecg.modules.seata.service.ProductService;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.stereotype.Service;

  10. @Slf4j
  11. @Service
  12. public class ProductServiceImpl implements ProductService {

  13.     @Autowired
  14.     private ProductMapper productMapper;

  15.     @Override
  16.     @DS(value = "product-ds")
  17.     public Product reduceStock(Integer productId, Integer amount) throws Exception {
  18.         log.info("当前 XID: {}", RootContext.getXID());

  19.         // 检查库存
  20.         Product product = productMapper.selectByPrimaryKey(productId);
  21.         if (product.getStock() < amount) {
  22.             throw new Exception("库存不足");
  23.         }

  24.         // 扣减库存
  25.         int updateCount = productMapper.reduceStock(productId, amount);
  26.         // 扣除成功
  27.         if (updateCount == 0) {
  28.             throw new Exception("库存不足");
  29.         }

  30.         // 扣除成功
  31.         log.info("扣除 {} 库存成功", productId);
  32.         return product;
  33.     }
  34. }
复制代码
  1. package org.jeecg.modules.seata.service.impl;

  2. import com.baomidou.dynamic.datasource.annotation.DS;
  3. import io.seata.core.context.RootContext;
  4. import io.seata.spring.annotation.GlobalTransactional;
  5. import lombok.extern.slf4j.Slf4j;
  6. import org.jeecg.modules.seata.entity.Orders;
  7. import org.jeecg.modules.seata.entity.Product;
  8. import org.jeecg.modules.seata.mapper.OrdersMapper;
  9. import org.jeecg.modules.seata.service.AccountService;
  10. import org.jeecg.modules.seata.service.OrderService;
  11. import org.jeecg.modules.seata.service.ProductService;
  12. import org.springframework.beans.factory.annotation.Autowired;
  13. import org.springframework.stereotype.Service;

  14. import java.math.BigDecimal;

  15. @Slf4j
  16. @Service
  17. public class OrderServiceImpl implements OrderService {

  18.     @Autowired
  19.     private OrdersMapper ordersMapper;

  20.     @Autowired
  21.     private AccountService accountService;

  22.     @Autowired
  23.     private ProductService productService;

  24.     @Override
  25.     @DS(value = "order-ds")
  26.     @GlobalTransactional //seata全局事务注解
  27.     public Integer createOrder(Integer userId, Integer productId) throws Exception {
  28.         Integer amount = 1; // 购买数量暂时设置为 1

  29.         log.info("当前 XID: {}", RootContext.getXID());

  30.         // 减库存 - 远程服务
  31.         Product product = productService.reduceStock(productId, amount);

  32.         // 减余额 - 远程服务
  33.         accountService.reduceBalance(userId, product.getPrice());

  34.         // 下订单 - 本地下订单
  35.         Orders order = new Orders();
  36.         order.setUserId(userId);
  37.         order.setProductId(productId);
  38.         order.setPayAmount(product.getPrice().multiply(new BigDecimal(amount)));

  39.         ordersMapper.insertSelective(order);

  40.         log.info("下订单: {}", order.getId());


  41.         //int a = 1/0;
  42.         // 返回订单编号
  43.         return order.getId();
  44.     }
  45. }
复制代码
  1. package org.jeecg.modules.seata.service.impl;

  2. import com.baomidou.dynamic.datasource.annotation.DS;
  3. import io.seata.core.context.RootContext;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.jeecg.modules.seata.entity.Account;
  6. import org.jeecg.modules.seata.mapper.AccountMapper;
  7. import org.jeecg.modules.seata.service.AccountService;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.stereotype.Service;

  10. import java.math.BigDecimal;

  11. @Slf4j
  12. @Service
  13. public class AccountServiceImpl implements AccountService {

  14.     @Autowired
  15.     private AccountMapper accountMapper;

  16.     @Override
  17.     @DS(value = "account-ds")
  18.     public void reduceBalance(Integer userId, BigDecimal money) throws Exception {
  19.         log.info("当前 XID: {}", RootContext.getXID());

  20.         // 检查余额
  21.         Account account = accountMapper.selectAccountByUserId(userId);
  22.         if (account.getBalance().doubleValue() < money.doubleValue()) {
  23.             throw new Exception("余额不足");
  24.         }

  25.         // 扣除余额
  26.         int updateCount = accountMapper.reduceBalance(userId, money);
  27.         // 扣除成功
  28.         if (updateCount == 0) {
  29.             throw new Exception("余额不足");
  30.         }
  31.         log.info("扣除用户 {} 余额成功", userId);
  32.     }
  33. }
复制代码

controller对应的代码如下

  1. package org.jeecg.modules.seata.controller;

  2. import lombok.extern.slf4j.Slf4j;
  3. import org.jeecg.modules.seata.service.OrderService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import org.springframework.web.bind.annotation.RequestParam;
  7. import org.springframework.web.bind.annotation.RestController;

  8. @Slf4j //lombok
  9. @RestController
  10. public class OrderController {

  11.     @Autowired
  12.     private OrderService orderService;

  13.     @RequestMapping("/order")
  14.     public Integer createOrder(@RequestParam("userId") Integer userId,
  15.                                @RequestParam("productId") Integer productId) throws Exception {

  16.         log.info("请求下单, 用户:{}, 商品:{}", userId, productId);

  17.         return orderService.createOrder(userId, productId);
  18.     }
  19. }
复制代码

7. 测试结果

在浏览器请求

http://localhost:8080/jeecg-boot/order?userId=1&productId=1

正常提交,数据库数据都是正常的。

http://localhost:8080/jeecg-boot/order?userId=2&productId=1

更新异常,数据回滚。


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表