RocketMQ 实战:模拟电商网站场景综合案例(三)

一、环境搭建:数据库表结构介绍

1、优惠券表

FieldTypeComment
coupon_iddecigint(50) NOT NULL优惠券ID
coupon_pricedecimal(10,2) NULL优惠券金额
user idbigint(50) NULL用户ID
order idbigint(32)NULL订单ID
is usedint(1) NULL是否使用 0未使用 1已使用
used timetimestamp NULL使用时间

2、商品表

FieldTypeComment
goods idbigint(50) NOT NULL主键
goods namevarchar(255) NULL商品名称
goods numberint(11) NULL商品库存
goods_pricedecimal(10,2) NULL商品价格
goods descvarchar(255) NULL商品描述
add timetimestamp NULL添加时间

3、订单表

FieldTypeComment
order _idbigint(50) NOT NULL订单ID
user _idbigint(50) NULL用户ID
order statusint(1) NULL订单状态 0未确认 1己确认 2已取消 3无效 4退款
pay_statusint(1) NULL支付状态 0未支付 1支付中 2已支付
shipping_statusint(1) NULL发货状态 0未发货 1已发货 2已退货
addressvarchar(255) NULL收货地址
consigneevarchar(255)NULL收货人
goods idbigint(50) NULL商品ID
goods numberint(11) NULL商品数量
goods pricedecimal(10,2) NULL商品价格
goods amountdecimal(10,0) NULL商品总价
shipping_feedecimal(10,2) NULL运费
order amountdecimal(10,2) NULL订单价格
coupon_idbigint(50) NULL优惠券ID
coupon_paiddecimal(10,2) NULL优惠券
money_paiddecimal(10,2) NULL已付金额
pay_amountdecimal(10,2) NULL支付金额
add timetimestamp NULL创建时间

4、订单商品日志表

FieldTypeComment
goods idint(11) NOT NULL商品ID
order idvarchar(32) NOT NULL订单ID
goods numberint(11) NULL库存数量
log_timedatetime NULL记录时间

5、用户表

FieldTypeComment
user idbigint(50) NOT NULL用户ID
user_namevarchar(255) NULL用户姓名
user passwordvarchar(255)NULL用户密码
user mobilevarchar(255)NULL手机号
user scoreint(11)NULL积分
user_reg_timetimestamp NULL注册时间
user moneydecimal(10,0)NULL用户余额

6、用户余额日志表

FieldTypeComment
Juser idbigint(50) NOT NULL用户ID
order idbigint(50) NOT NULL订单ID
money_log_typeint(1) NOT NULL日志类型 1订单付款 2 订单退款
use moneydecimal(10,2) NULL操作金额
create timetimestamp NULL日志时间

7、订单支付表

FieldTypeComment
pay_idbigint(50) NOT NULL支付编号
order idbigint(50) NULL订单编号
pay_amountdecimal(10,2) NULL支付金额
is_paidint(1) NULL是否已支付 1否 2是

8、MQ 消息生产表

FieldTypeComment
idvarchar(100) NOT NULL主键
group _namevarchar(100) NULL生产者组名
msg_topicvarchar(100) NULL消息主题
msg_tagvarchar(100) NULLTag
msg_keyvarchar(100) NULLKey
hsg bodyvarchar(500) NULL消息内容
msg statusint(1)NULL0:未处理;1:已经处理
create timetimestamp NOT NULL记录时间

9、MQ 消息消费表

FieldTypeComment
msg_idvarchar(50) NULL消息ID
group namevarchar(100) NOT NULL消费者组名
msg_tagvarchar(100) NOT NULLTag
msg_keyvarchar(100) NOT NULLKey
msg_bodyvarchar(500) NULL消息体
consumer statusint(1) NULL0:正在处理;1:处理成功;2:处理失败
consumer timesint(1)NULL消费次数
consumer_timestamptimestamp NULL消费时间
remarkvarchar(500)NULL备注

10、在 mysql 中,创建这 9 张表。

1)用 SQLyong 连接 mysql 数据库


保存的连接:localhost  
MySQL Host Address : 如:localhost
用户名:root
密码:root
端口:3306

【连接】

2)创建 trade 数据库。

3)创建 9 个 数据表。


/*
SQLyog Ultimate v8.32 
MySQL - 5.5.49 : Database - trade
*********************************************************************
*/


/*!40101 SET NAMES utf8 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`trade` /*!40100 DEFAULT CHARACTER SET utf8 */;

USE `trade`;

/*Table structure for table `trade_coupon` */

DROP TABLE IF EXISTS `trade_coupon`;

CREATE TABLE `trade_coupon` (
  `coupon_id` bigint(50) NOT NULL COMMENT '优惠券ID',
  `coupon_price` decimal(10,2) DEFAULT NULL COMMENT '优惠券金额',
  `user_id` bigint(50) DEFAULT NULL COMMENT '用户ID',
  `order_id` bigint(32) DEFAULT NULL COMMENT '订单ID',
  `is_used` int(1) DEFAULT NULL COMMENT '是否使用 0未使用 1已使用',
  `used_time` timestamp NULL DEFAULT NULL COMMENT '使用时间',
  PRIMARY KEY (`coupon_id`),
  KEY `FK_trade_coupon` (`user_id`),
  KEY `FK_trade_coupon2` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Data for the table `trade_coupon` */

/*Table structure for table `trade_goods` */

DROP TABLE IF EXISTS `trade_goods`;

CREATE TABLE `trade_goods` (
  `goods_id` bigint(50) NOT NULL AUTO_INCREMENT,
  `goods_name` varchar(255) DEFAULT NULL COMMENT '商品名称',
  `goods_number` int(11) DEFAULT NULL COMMENT '商品库存',
  `goods_price` decimal(10,2) DEFAULT NULL COMMENT '商品价格',
  `goods_desc` varchar(255) DEFAULT NULL COMMENT '商品描述',
  `add_time` timestamp NULL DEFAULT NULL COMMENT '添加时间',
  PRIMARY KEY (`goods_id`)
) ENGINE=InnoDB AUTO_INCREMENT=345959443973935105 DEFAULT CHARSET=utf8;

/*Data for the table `trade_goods` */

insert  into `trade_goods`(`goods_id`,`goods_name`,`goods_number`,`goods_price`,`goods_desc`,`add_time`) values (345959443973935104,'华为P30',999,'5000.00','夜间拍照更美','2019-07-09 20:38:00');

/*Table structure for table `trade_goods_number_log` */

DROP TABLE IF EXISTS `trade_goods_number_log`;

CREATE TABLE `trade_goods_number_log` (
  `goods_id` bigint(50) NOT NULL COMMENT '商品ID',
  `order_id` bigint(50) NOT NULL COMMENT '订单ID',
  `goods_number` int(11) DEFAULT NULL COMMENT '库存数量',
  `log_time` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`goods_id`,`order_id`),
  KEY `FK_trade_goods_number_log2` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Data for the table `trade_goods_number_log` */

/*Table structure for table `trade_mq_consumer_log` */

DROP TABLE IF EXISTS `trade_mq_consumer_log`;

CREATE TABLE `trade_mq_consumer_log` (
  `msg_id` varchar(50) DEFAULT NULL,
  `group_name` varchar(100) NOT NULL,
  `msg_tag` varchar(100) NOT NULL,
  `msg_key` varchar(100) NOT NULL,
  `msg_body` varchar(500) DEFAULT NULL,
  `consumer_status` int(1) DEFAULT NULL COMMENT '0:正在处理;1:处理成功;2:处理失败',
  `consumer_times` int(1) DEFAULT NULL,
  `consumer_timestamp` timestamp NULL DEFAULT NULL,
  `remark` varchar(500) DEFAULT NULL,
  PRIMARY KEY (`group_name`,`msg_tag`,`msg_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Data for the table `trade_mq_consumer_log` */

/*Table structure for table `trade_mq_producer_temp` */

DROP TABLE IF EXISTS `trade_mq_producer_temp`;

CREATE TABLE `trade_mq_producer_temp` (
  `id` varchar(100) NOT NULL,
  `group_name` varchar(100) DEFAULT NULL,
  `msg_topic` varchar(100) DEFAULT NULL,
  `msg_tag` varchar(100) DEFAULT NULL,
  `msg_key` varchar(100) DEFAULT NULL,
  `msg_body` varchar(500) DEFAULT NULL,
  `msg_status` int(1) DEFAULT NULL COMMENT '0:未处理;1:已经处理',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Data for the table `trade_mq_producer_temp` */

/*Table structure for table `trade_order` */

DROP TABLE IF EXISTS `trade_order`;

CREATE TABLE `trade_order` (
  `order_id` bigint(50) NOT NULL COMMENT '订单ID',
  `user_id` bigint(50) DEFAULT NULL COMMENT '用户ID',
  `order_status` int(1) DEFAULT NULL COMMENT '订单状态 0未确认 1已确认 2已取消 3无效 4退款',
  `pay_status` int(1) DEFAULT NULL COMMENT '支付状态 0未支付 1支付中 2已支付',
  `shipping_status` int(1) DEFAULT NULL COMMENT '发货状态 0未发货 1已发货 2已收货',
  `address` varchar(255) DEFAULT NULL COMMENT '收货地址',
  `consignee` varchar(255) DEFAULT NULL COMMENT '收货人',
  `goods_id` bigint(50) DEFAULT NULL COMMENT '商品ID',
  `goods_number` int(11) DEFAULT NULL COMMENT '商品数量',
  `goods_price` decimal(10,2) DEFAULT NULL COMMENT '商品价格',
  `goods_amount` decimal(10,0) DEFAULT NULL COMMENT '商品总价',
  `shipping_fee` decimal(10,2) DEFAULT NULL COMMENT '运费',
  `order_amount` decimal(10,2) DEFAULT NULL COMMENT '订单价格',
  `coupon_id` bigint(50) DEFAULT NULL COMMENT '优惠券ID',
  `coupon_paid` decimal(10,2) DEFAULT NULL COMMENT '优惠券',
  `money_paid` decimal(10,2) DEFAULT NULL COMMENT '已付金额',
  `pay_amount` decimal(10,2) DEFAULT NULL COMMENT '支付金额',
  `add_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  `confirm_time` timestamp NULL DEFAULT NULL COMMENT '订单确认时间',
  `pay_time` timestamp NULL DEFAULT NULL COMMENT '支付时间',
  PRIMARY KEY (`order_id`),
  KEY `FK_trade_order` (`user_id`),
  KEY `FK_trade_order2` (`goods_id`),
  KEY `FK_trade_order3` (`coupon_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Data for the table `trade_order` */

/*Table structure for table `trade_pay` */

DROP TABLE IF EXISTS `trade_pay`;

CREATE TABLE `trade_pay` (
  `pay_id` bigint(50) NOT NULL COMMENT '支付编号',
  `order_id` bigint(50) DEFAULT NULL COMMENT '订单编号',
  `pay_amount` decimal(10,2) DEFAULT NULL COMMENT '支付金额',
  `is_paid` int(1) DEFAULT NULL COMMENT '是否已支付 1否 2是',
  PRIMARY KEY (`pay_id`),
  KEY `FK_trade_pay` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Data for the table `trade_pay` */

/*Table structure for table `trade_user` */

DROP TABLE IF EXISTS `trade_user`;

CREATE TABLE `trade_user` (
  `user_id` bigint(50) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `user_name` varchar(255) DEFAULT NULL COMMENT '用户姓名',
  `user_password` varchar(255) DEFAULT NULL COMMENT '用户密码',
  `user_mobile` varchar(255) DEFAULT NULL COMMENT '手机号',
  `user_score` int(11) DEFAULT NULL COMMENT '积分',
  `user_reg_time` timestamp NULL DEFAULT NULL COMMENT '注册时间',
  `user_money` decimal(10,0) DEFAULT NULL COMMENT '用户余额',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=345963634385633281 DEFAULT CHARSET=utf8;

/*Data for the table `trade_user` */

insert  into `trade_user`(`user_id`,`user_name`,`user_password`,`user_mobile`,`user_score`,`user_reg_time`,`user_money`) values (345963634385633280,'刘备','123L','18888888888L',100,'2019-07-09 13:37:03','900');

/*Table structure for table `trade_user_money_log` */

DROP TABLE IF EXISTS `trade_user_money_log`;

CREATE TABLE `trade_user_money_log` (
  `user_id` bigint(50) NOT NULL COMMENT '用户ID',
  `order_id` bigint(50) NOT NULL COMMENT '订单ID',
  `money_log_type` int(1) NOT NULL COMMENT '日志类型 1订单付款 2 订单退款',
  `use_money` decimal(10,2) DEFAULT NULL,
  `create_time` timestamp NULL DEFAULT NULL COMMENT '日志时间',
  PRIMARY KEY (`user_id`,`order_id`,`money_log_type`),
  KEY `FK_trade_user_money_log2` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Data for the table `trade_user_money_log` */

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;



二、项目工程初始化

1、创建 shop-parent 父工程,并在 pom.xml 中,导入依赖坐标。


<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.iteima.shop</groupId>
    <artifactId>shop-parent</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>


    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
    </parent>


    <properties>
        <rocketmq-spring-boot-starter-version>2.0.3</rocketmq-spring-boot-starter-version>
    </properties>

    <dependencies>
        <!--dubbo-->
        <dependency>
            <groupId>com.alibaba.spring.boot</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>
        <!--spring-boot-stater-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>log4j-to-slf4j</artifactId>
                    <groupId>org.apache.logging.log4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--zookeeper-->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.10</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>


        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.9</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-log4j12</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>


        <!--Test-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>


        <!--RocketMQ-->
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
            <version>${rocketmq-spring-boot-starter-version}</version>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.6</version>
        </dependency>

    </dependencies>

    <modules>
        <module>../shop-order-web</module>
        <module>../shop-coupon-service</module>
        <module>../shop-user-service</module>
        <module>../shop-order-service</module>
        <module>../shop-pay-web</module>
        <module>../shop-pojo</module>
        <module>../shop-common</module>
        <module>../shop-api</module>
        <module>../shop-goods-service</module>
        <module>../shop-pay-service</module>
    </modules>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <target>1.8</target>
                    <source>1.8</source>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
<!-- D:\java-test\idea2019\shop\shop-parent\pom.xml -->

2、创建 shop-api 接口层 工程。

2.1、在 pom.xml 中,导入依赖坐标。

<?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>shop-parent</artifactId>
        <groupId>com.iteima.shop</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../shop-parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>shop-api</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-pojo</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>
<!-- D:\java-test\idea2019\shop\shop-api\pom.xml -->

2.2、创建 优惠券接口 类 ICouponService.java
package com.itheima.api;

import com.itheima.entity.Result;
import com.itheima.shop.pojo.TradeCoupon;

/**
 * 优惠券接口
 */
public interface ICouponService {


    /**
     * 根据ID查询优惠券对象
     * @param coupouId
     * @return
     */
    public TradeCoupon findOne(Long coupouId);

    /**
     * 更细优惠券状态
     * @param coupon
     * @return
     */
    Result updateCouponStatus(TradeCoupon coupon);
}

2.3、创建 接口 类 IGoodsService.java
package com.itheima.api;

import com.itheima.entity.Result;
import com.itheima.shop.pojo.TradeGoods;
import com.itheima.shop.pojo.TradeGoodsNumberLog;

public interface IGoodsService {
    /**
     * 根据ID查询商品对象
     * @param goodsId
     * @return
     */
    TradeGoods findOne(Long goodsId);

    /**
     * 扣减库存
     * @param goodsNumberLog
     * @return
     */
    Result reduceGoodsNum(TradeGoodsNumberLog goodsNumberLog);
}

2.4、创建 接口 类 IOrderService.java
package com.itheima.api;

import com.itheima.entity.Result;
import com.itheima.shop.pojo.TradeOrder;

public interface IOrderService {

    /**
     * 下单接口
     * @param order
     * @return
     */
    public Result confirmOrder(TradeOrder order);

}

2.5、创建 接口 类 IPayService.java
package com.itheima.api;

import com.itheima.entity.Result;
import com.itheima.shop.pojo.TradePay;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.remoting.exception.RemotingException;

public interface IPayService {

    public Result createPayment(TradePay tradePay);

    public Result callbackPayment(TradePay tradePay) throws InterruptedException, RemotingException, MQClientException, MQBrokerException;

}

2.6、创建 接口 类 IUserService.java
package com.itheima.api;

import com.itheima.entity.Result;
import com.itheima.shop.pojo.TradeUser;
import com.itheima.shop.pojo.TradeUserMoneyLog;

public interface IUserService {
    TradeUser findOne(Long userId);

    Result updateMoneyPaid(TradeUserMoneyLog userMoneyLog);
}

3、创建 shop-common 工具工程。

3.1、在 pom.xml 中,导入依赖坐标。

<?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>shop-parent</artifactId>
        <groupId>com.iteima.shop</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../shop-parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>shop-common</artifactId>

</project>
<!-- D:\java-test\idea2019\shop\shop-common\pom.xml -->

3.2、创建 类 ShopCode.java
package com.itheima.constant;

/**
 * @author Think
 */

public enum ShopCode {
    //正确
    SHOP_SUCCESS(true, 1, "正确"),
    //错误
    SHOP_FAIL(false, 0, "错误"),

    //付款
    SHOP_USER_MONEY_PAID(true, 1, "付款"),
    //退款
    SHOP_USER_MONEY_REFUND(true, 2, "退款"),
    //订单未确认
    SHOP_ORDER_NO_CONFIRM(false, 0, "订单未确认"),
    //订单已确认
    SHOP_ORDER_CONFIRM(true, 1, "订单已经确认"),
    //订单已取消
    SHOP_ORDER_CANCEL(false, 2, "订单已取消"),
    //订单已取消
    SHOP_ORDER_INVALID(false, 3, "订单无效"),
    //订单已取消
    SHOP_ORDER_RETURNED(false, 4, "订单已退货"),
    //订单已付款
    SHOP_ORDER_PAY_STATUS_NO_PAY(true,0,"订单未付款"),
    //订单已付款
    SHOP_ORDER_PAY_STATUS_PAYING(true,1,"订单正在付款"),
    //订单已付款
    SHOP_ORDER_PAY_STATUS_IS_PAY(true,2,"订单已付款"),
    //消息正在处理
    SHOP_MQ_MESSAGE_STATUS_PROCESSING(true, 0, "消息正在处理"),
    //消息处理成功
    SHOP_MQ_MESSAGE_STATUS_SUCCESS(true, 1, "消息处理成功"),
    //消息处理失败
    SHOP_MQ_MESSAGE_STATUS_FAIL(false, 2, "消息处理失败"),
    //请求参数有误
    SHOP_REQUEST_PARAMETER_VALID(false, -1, "请求参数有误"),
    //优惠券已经使用
    SHOP_COUPON_ISUSED(true, 1, "优惠券已经使用"),
    //优惠券未使用
    SHOP_COUPON_UNUSED(false, 0, "优惠券未使用"),
    //快递运费不正确
    SHOP_ORDER_STATUS_UPDATE_FAIL(false, 10001, "订单状态修改失败"),
    //快递运费不正确
    SHOP_ORDER_SHIPPINGFEE_INVALID(false, 10002, "订单运费不正确"),
    //订单总价格不合法
    SHOP_ORDERAMOUNT_INVALID(false, 10003, "订单总价格不正确"),
    //订单保存失败
    SHOP_ORDER_SAVE_ERROR(false, 10004, "订单保存失败"),
    //订单确认失败
    SHOP_ORDER_CONFIRM_FAIL(false, 10005, "订单确认失败"),
    //商品不存在
    SHOP_GOODS_NO_EXIST(false, 20001, "商品不存在"),
    //订单价格非法
    SHOP_GOODS_PRICE_INVALID(false, 20002, "商品价格非法"),
    //商品库存不足
    SHOP_GOODS_NUM_NOT_ENOUGH(false, 20003, "商品库存不足"),
    //扣减库存失败
    SHOP_REDUCE_GOODS_NUM_FAIL(false, 20004, "扣减库存失败"),
    //库存记录为空
    SHOP_REDUCE_GOODS_NUM_EMPTY(false, 20005, "扣减库存失败"),
    //用户账号不能为空
    SHOP_USER_IS_NULL(false, 30001, "用户账号不能为空"),
    //用户信息不存在
    SHOP_USER_NO_EXIST(false, 30002, "用户不存在"),
    //余额扣减失败
    SHOP_USER_MONEY_REDUCE_FAIL(false, 30003, "余额扣减失败"),
    //已经退款
    SHOP_USER_MONEY_REFUND_ALREADY(true, 30004, "订单已经退过款"),
    //优惠券不不存在
    SHOP_COUPON_NO_EXIST(false, 40001, "优惠券不存在"),
    //优惠券不合法
    SHOP_COUPON_INVALIED(false, 40002, "优惠券不合法"),
    //优惠券使用失败
    SHOP_COUPON_USE_FAIL(false, 40003, "优惠券使用失败"),
    //余额不能小于0
    SHOP_MONEY_PAID_LESS_ZERO(false, 50001, "余额不能小于0"),
    //余额非法
    SHOP_MONEY_PAID_INVALID(false, 50002, "余额非法"),
    //Topic不能为空
    SHOP_MQ_TOPIC_IS_EMPTY(false, 60001, "Topic不能为空"),
    //消息体不能为空
    SHOP_MQ_MESSAGE_BODY_IS_EMPTY(false, 60002, "消息体不能为空"),
    //消息发送失败
    SHOP_MQ_SEND_MESSAGE_FAIL(false,60003,"消息发送失败"),
    //支付订单未找到
    SHOP_PAYMENT_NOT_FOUND(false,70001,"支付订单未找到"),
    //支付订单已支付
    SHOP_PAYMENT_IS_PAID(false,70002,"支付订单已支付"),
    //订单付款失败
    SHOP_PAYMENT_PAY_ERROR(false,70002,"订单支付失败");


    Boolean success;
    Integer code;
    String message;

    ShopCode() {

    }

    ShopCode(Boolean success, Integer code, String message) {
        this.success = success;
        this.code = code;
        this.message = message;
    }

    public Boolean getSuccess() {
        return success;
    }

    public void setSuccess(Boolean success) {
        this.success = success;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public String toString() {
        return "ShopCode{" +
                "success=" + success +
                ", code=" + code +
                ", message='" + message + '\'' +
                '}';
    }
}

3.3、创建 类 CastException.java
package com.itheima.exception;

import com.itheima.constant.ShopCode;
import lombok.extern.slf4j.Slf4j;

/**
 * 异常抛出类
 */
@Slf4j
public class CastException {
    public static void cast(ShopCode shopCode) {
        log.error(shopCode.toString());
        throw new CustomerException(shopCode);
    }
}

3.4、创建 类 CustomerException.java
package com.itheima.exception;

import com.itheima.constant.ShopCode;

/**
 * 自定义异常
 */
public class CustomerException extends RuntimeException{

    private ShopCode shopCode;

    public CustomerException(ShopCode shopCode) {
        this.shopCode = shopCode;
    }
}

3.5、创建 类 IDWorker.java
package com.itheima.utils;

public class IDWorker {

    /**
     * 起始的时间戳
     */
    private final static long START_STMP = 1480166465631L;

    /**
     * 每一部分占用的位数
     */
    private final static long SEQUENCE_BIT = 12; //序列号占用的位数
    private final static long MACHINE_BIT = 5;   //机器标识占用的位数
    private final static long DATACENTER_BIT = 5;//数据中心占用的位数

    /**
     * 每一部分的最大值
     */
    private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
    private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
    private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);

    /**
     * 每一部分向左的位移
     */
    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;

    private long datacenterId;  //数据中心
    private long machineId;     //机器标识
    private long sequence = 0L; //序列号
    private long lastStmp = -1L;//上一次时间戳

    public IDWorker(long datacenterId, long machineId) {
        if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
            throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
        }
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
        }
        this.datacenterId = datacenterId;
        this.machineId = machineId;
    }

    /**
     * 产生下一个ID
     *
     * @return
     */
    public synchronized long nextId() {
        long currStmp = getNewstmp();
        if (currStmp < lastStmp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }

        if (currStmp == lastStmp) {
            //相同毫秒内,序列号自增
            sequence = (sequence + 1) & MAX_SEQUENCE;
            //同一毫秒的序列数已经达到最大
            if (sequence == 0L) {
                currStmp = getNextMill();
            }
        } else {
            //不同毫秒内,序列号置为0
            sequence = 0L;
        }

        lastStmp = currStmp;

        return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
                | datacenterId << DATACENTER_LEFT       //数据中心部分
                | machineId << MACHINE_LEFT             //机器标识部分
                | sequence;                             //序列号部分
    }

    private long getNextMill() {
        long mill = getNewstmp();
        while (mill <= lastStmp) {
            mill = getNewstmp();
        }
        return mill;
    }

    private long getNewstmp() {
        return System.currentTimeMillis();
    }

    public static void main(String[] args) {
        IDWorker idWorker = new IDWorker(2, 3);
        for (int i = 0; i < 10; i++) {
            System.out.println(idWorker.nextId());
        }
    }

}

4、创建 shop-coupon-service 优惠券服务 工程。

4.1、在 pom.xml 中,导入依赖坐标。

<?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>shop-parent</artifactId>
        <groupId>com.iteima.shop</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../shop-parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>shop-coupon-service</artifactId>

    <dependencies>

        <!--mybatis-springboot-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>

        <!-- MySQL连接驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-pojo</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>
<!-- D:\java-test\idea2019\shop\shop-coupon-service\pom.xml -->

4.2 创建 类 TradeCouponMapper.java
package com.itheima.shop.mapper;

import com.itheima.shop.pojo.TradeCoupon;
import com.itheima.shop.pojo.TradeCouponExample;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface TradeCouponMapper {
    int countByExample(TradeCouponExample example);

    int deleteByExample(TradeCouponExample example);

    int deleteByPrimaryKey(Long couponId);

    int insert(TradeCoupon record);

    int insertSelective(TradeCoupon record);

    List<TradeCoupon> selectByExample(TradeCouponExample example);

    TradeCoupon selectByPrimaryKey(Long couponId);

    int updateByExampleSelective(@Param("record") TradeCoupon record, @Param("example") TradeCouponExample example);

    int updateByExample(@Param("record") TradeCoupon record, @Param("example") TradeCouponExample example);

    int updateByPrimaryKeySelective(TradeCoupon record);

    int updateByPrimaryKey(TradeCoupon record);
}
4.3 创建 类 CancelMQListener.java
package com.itheima.shop.mq;

import com.alibaba.fastjson.JSON;
import com.itheima.constant.ShopCode;
import com.itheima.entity.MQEntity;
import com.itheima.shop.mapper.TradeCouponMapper;
import com.itheima.shop.pojo.TradeCoupon;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;


@Slf4j
@Component
@RocketMQMessageListener(topic = "${mq.order.topic}", consumerGroup = "${mq.order.consumer.group.name}", messageModel = MessageModel.BROADCASTING)
public class CancelMQListener implements RocketMQListener<MessageExt> {


    @Autowired
    private TradeCouponMapper couponMapper;

    @Override
    public void onMessage(MessageExt message) {

        try {
            //1. 解析消息内容
            String body = new String(message.getBody(), "UTF-8");
            MQEntity mqEntity = JSON.parseObject(body, MQEntity.class);
            log.info("接收到消息");
            if(mqEntity.getCouponId()!=null){
                //2. 查询优惠券信息
                TradeCoupon coupon = couponMapper.selectByPrimaryKey(mqEntity.getCouponId());
                //3.更改优惠券状态
                coupon.setUsedTime(null);
                coupon.setIsUsed(ShopCode.SHOP_COUPON_UNUSED.getCode());
                coupon.setOrderId(null);
                couponMapper.updateByPrimaryKey(coupon);
            }
            log.info("回退优惠券成功");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            log.error("回退优惠券失败");
        }

    }
}

4.4 创建 类 CouponServiceImpl.java
package com.itheima.shop.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.itheima.api.ICouponService;
import com.itheima.constant.ShopCode;
import com.itheima.entity.Result;
import com.itheima.exception.CastException;
import com.itheima.shop.mapper.TradeCouponMapper;
import com.itheima.shop.pojo.TradeCoupon;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@Service(interfaceClass = ICouponService.class)
public class CouponServiceImpl implements ICouponService{

    @Autowired
    private TradeCouponMapper couponMapper;

    @Override
    public TradeCoupon findOne(Long coupouId) {
        if(coupouId==null){
            CastException.cast(ShopCode.SHOP_REQUEST_PARAMETER_VALID);
        }

        return couponMapper.selectByPrimaryKey(coupouId);
    }

    @Override
    public Result updateCouponStatus(TradeCoupon coupon) {
        if(coupon==null||coupon.getCouponId()==null){
            CastException.cast(ShopCode.SHOP_REQUEST_PARAMETER_VALID);
        }
        //更新优惠券状态
        couponMapper.updateByPrimaryKey(coupon);
        return new Result(ShopCode.SHOP_SUCCESS.getSuccess(),ShopCode.SHOP_SUCCESS.getMessage());
    }
}

4.5 创建 类 CouponServiceApplication.java
package com.itheima.shop;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubboConfiguration
public class CouponServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(CouponServiceApplication.class,args);
    }

}

4.6 创建 TradeCouponMapper.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.itheima.shop.mapper.TradeCouponMapper" >
  <resultMap id="BaseResultMap" type="com.itheima.shop.pojo.TradeCoupon" >
    <id column="coupon_id" property="couponId" jdbcType="BIGINT" />
    <result column="coupon_price" property="couponPrice" jdbcType="DECIMAL" />
    <result column="user_id" property="userId" jdbcType="BIGINT" />
    <result column="order_id" property="orderId" jdbcType="BIGINT" />
    <result column="is_used" property="isUsed" jdbcType="INTEGER" />
    <result column="used_time" property="usedTime" jdbcType="TIMESTAMP" />
  </resultMap>
  <sql id="Example_Where_Clause" >
    <where >
      <foreach collection="oredCriteria" item="criteria" separator="or" >
        <if test="criteria.valid" >
          <trim prefix="(" suffix=")" prefixOverrides="and" >
            <foreach collection="criteria.criteria" item="criterion" >
              <choose >
                <when test="criterion.noValue" >
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue" >
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue" >
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue" >
                  and ${criterion.condition}
                  <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Update_By_Example_Where_Clause" >
    <where >
      <foreach collection="example.oredCriteria" item="criteria" separator="or" >
        <if test="criteria.valid" >
          <trim prefix="(" suffix=")" prefixOverrides="and" >
            <foreach collection="criteria.criteria" item="criterion" >
              <choose >
                <when test="criterion.noValue" >
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue" >
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue" >
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue" >
                  and ${criterion.condition}
                  <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Base_Column_List" >
    coupon_id, coupon_price, user_id, order_id, is_used, used_time
  </sql>
  <select id="selectByExample" resultMap="BaseResultMap" parameterType="com.itheima.shop.pojo.TradeCouponExample" >
    select
    <if test="distinct" >
      distinct
    </if>
    <include refid="Base_Column_List" />
    from trade_coupon
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
    <if test="orderByClause != null" >
      order by ${orderByClause}
    </if>
  </select>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select 
    <include refid="Base_Column_List" />
    from trade_coupon
    where coupon_id = #{couponId,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
    delete from trade_coupon
    where coupon_id = #{couponId,jdbcType=BIGINT}
  </delete>
  <delete id="deleteByExample" parameterType="com.itheima.shop.pojo.TradeCouponExample" >
    delete from trade_coupon
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
  </delete>
  <insert id="insert" parameterType="com.itheima.shop.pojo.TradeCoupon" >
    insert into trade_coupon (coupon_id, coupon_price, user_id, 
      order_id, is_used, used_time
      )
    values (#{couponId,jdbcType=BIGINT}, #{couponPrice,jdbcType=DECIMAL}, #{userId,jdbcType=BIGINT}, 
      #{orderId,jdbcType=BIGINT}, #{isUsed,jdbcType=INTEGER}, #{usedTime,jdbcType=TIMESTAMP}
      )
  </insert>
  <insert id="insertSelective" parameterType="com.itheima.shop.pojo.TradeCoupon" >
    insert into trade_coupon
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="couponId != null" >
        coupon_id,
      </if>
      <if test="couponPrice != null" >
        coupon_price,
      </if>
      <if test="userId != null" >
        user_id,
      </if>
      <if test="orderId != null" >
        order_id,
      </if>
      <if test="isUsed != null" >
        is_used,
      </if>
      <if test="usedTime != null" >
        used_time,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="couponId != null" >
        #{couponId,jdbcType=BIGINT},
      </if>
      <if test="couponPrice != null" >
        #{couponPrice,jdbcType=DECIMAL},
      </if>
      <if test="userId != null" >
        #{userId,jdbcType=BIGINT},
      </if>
      <if test="orderId != null" >
        #{orderId,jdbcType=BIGINT},
      </if>
      <if test="isUsed != null" >
        #{isUsed,jdbcType=INTEGER},
      </if>
      <if test="usedTime != null" >
        #{usedTime,jdbcType=TIMESTAMP},
      </if>
    </trim>
  </insert>
  <select id="countByExample" parameterType="com.itheima.shop.pojo.TradeCouponExample" resultType="java.lang.Integer" >
    select count(*) from trade_coupon
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
  </select>
  <update id="updateByExampleSelective" parameterType="map" >
    update trade_coupon
    <set >
      <if test="record.couponId != null" >
        coupon_id = #{record.couponId,jdbcType=BIGINT},
      </if>
      <if test="record.couponPrice != null" >
        coupon_price = #{record.couponPrice,jdbcType=DECIMAL},
      </if>
      <if test="record.userId != null" >
        user_id = #{record.userId,jdbcType=BIGINT},
      </if>
      <if test="record.orderId != null" >
        order_id = #{record.orderId,jdbcType=BIGINT},
      </if>
      <if test="record.isUsed != null" >
        is_used = #{record.isUsed,jdbcType=INTEGER},
      </if>
      <if test="record.usedTime != null" >
        used_time = #{record.usedTime,jdbcType=TIMESTAMP},
      </if>
    </set>
    <if test="_parameter != null" >
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByExample" parameterType="map" >
    update trade_coupon
    set coupon_id = #{record.couponId,jdbcType=BIGINT},
      coupon_price = #{record.couponPrice,jdbcType=DECIMAL},
      user_id = #{record.userId,jdbcType=BIGINT},
      order_id = #{record.orderId,jdbcType=BIGINT},
      is_used = #{record.isUsed,jdbcType=INTEGER},
      used_time = #{record.usedTime,jdbcType=TIMESTAMP}
    <if test="_parameter != null" >
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByPrimaryKeySelective" parameterType="com.itheima.shop.pojo.TradeCoupon" >
    update trade_coupon
    <set >
      <if test="couponPrice != null" >
        coupon_price = #{couponPrice,jdbcType=DECIMAL},
      </if>
      <if test="userId != null" >
        user_id = #{userId,jdbcType=BIGINT},
      </if>
      <if test="orderId != null" >
        order_id = #{orderId,jdbcType=BIGINT},
      </if>
      <if test="isUsed != null" >
        is_used = #{isUsed,jdbcType=INTEGER},
      </if>
      <if test="usedTime != null" >
        used_time = #{usedTime,jdbcType=TIMESTAMP},
      </if>
    </set>
    where coupon_id = #{couponId,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.itheima.shop.pojo.TradeCoupon" >
    update trade_coupon
    set coupon_price = #{couponPrice,jdbcType=DECIMAL},
      user_id = #{userId,jdbcType=BIGINT},
      order_id = #{orderId,jdbcType=BIGINT},
      is_used = #{isUsed,jdbcType=INTEGER},
      used_time = #{usedTime,jdbcType=TIMESTAMP}
    where coupon_id = #{couponId,jdbcType=BIGINT}
  </update>
</mapper>
4.7 创建 application.properties 配置文件。
# application.properties
spring.application.name=dubbo-coupon-provider
spring.dubbo.application.id=dubbo-coupon-provider
spring.dubbo.application.name=dubbo-coupon-provider
spring.dubbo.registry.address=zookeeper://192.168.25.140:2181;zookeeper://192.168.25.140:2182;zookeeper://192.168.25.140:2183
spring.dubbo.server=true
spring.dubbo.protocol.name=dubbo
spring.dubbo.protocol.port=20881


# DB
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/trade?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root

#spring集成Mybatis环境
#pojo别名扫描包
mybatis.type-aliases-package=com.itheima.shop.pojo
#加载Mybatis映射文件
mybatis.mapper-locations=classpath:com/itheima/shop/mapper/*Mapper.xml

# RocketMQ
rocketmq.name-server=192.168.25.135:9876;192.168.25.138:9876
rocketmq.producer.group=orderProducerGroup

mq.order.consumer.group.name=order_orderTopic_cancel_group
mq.order.topic=orderTopic

5、创建 shop-goods-service 商品服务 工程。

5.1、在 pom.xml 中,导入依赖坐标。

<?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>shop-parent</artifactId>
        <groupId>com.iteima.shop</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../shop-parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>shop-goods-service</artifactId>

    <dependencies>

        <!--mybatis-springboot-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>

        </dependency>

        <!-- MySQL连接驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-pojo</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-pojo</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

    </dependencies>

</project>
<!-- D:\java-test\idea2019\shop\shop-goods-service\pom.xml -->

5.2、 创建 接口 类 TradeGoodsMapper.java
package com.itheima.shop.mapper;

import com.itheima.shop.pojo.TradeGoods;
import com.itheima.shop.pojo.TradeGoodsExample;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface TradeGoodsMapper {
    int countByExample(TradeGoodsExample example);

    int deleteByExample(TradeGoodsExample example);

    int deleteByPrimaryKey(Long goodsId);

    int insert(TradeGoods record);

    int insertSelective(TradeGoods record);

    List<TradeGoods> selectByExample(TradeGoodsExample example);

    TradeGoods selectByPrimaryKey(Long goodsId);

    int updateByExampleSelective(@Param("record") TradeGoods record, @Param("example") TradeGoodsExample example);

    int updateByExample(@Param("record") TradeGoods record, @Param("example") TradeGoodsExample example);

    int updateByPrimaryKeySelective(TradeGoods record);

    int updateByPrimaryKey(TradeGoods record);
}
5.3、 创建 接口 类 TradeGoodsNumberLogMapper.java
package com.itheima.shop.mapper;

import com.itheima.shop.pojo.TradeGoodsNumberLog;
import com.itheima.shop.pojo.TradeGoodsNumberLogExample;
import com.itheima.shop.pojo.TradeGoodsNumberLogKey;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface TradeGoodsNumberLogMapper {
    int countByExample(TradeGoodsNumberLogExample example);

    int deleteByExample(TradeGoodsNumberLogExample example);

    int deleteByPrimaryKey(TradeGoodsNumberLogKey key);

    int insert(TradeGoodsNumberLog record);

    int insertSelective(TradeGoodsNumberLog record);

    List<TradeGoodsNumberLog> selectByExample(TradeGoodsNumberLogExample example);

    TradeGoodsNumberLog selectByPrimaryKey(TradeGoodsNumberLogKey key);

    int updateByExampleSelective(@Param("record") TradeGoodsNumberLog record, @Param("example") TradeGoodsNumberLogExample example);

    int updateByExample(@Param("record") TradeGoodsNumberLog record, @Param("example") TradeGoodsNumberLogExample example);

    int updateByPrimaryKeySelective(TradeGoodsNumberLog record);

    int updateByPrimaryKey(TradeGoodsNumberLog record);
}
5.4、 创建 接口 类 TradeMqConsumerLogMapper.java
package com.itheima.shop.mapper;

import com.itheima.shop.pojo.TradeMqConsumerLog;
import com.itheima.shop.pojo.TradeMqConsumerLogExample;
import com.itheima.shop.pojo.TradeMqConsumerLogKey;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface TradeMqConsumerLogMapper {
    int countByExample(TradeMqConsumerLogExample example);

    int deleteByExample(TradeMqConsumerLogExample example);

    int deleteByPrimaryKey(TradeMqConsumerLogKey key);

    int insert(TradeMqConsumerLog record);

    int insertSelective(TradeMqConsumerLog record);

    List<TradeMqConsumerLog> selectByExample(TradeMqConsumerLogExample example);

    TradeMqConsumerLog selectByPrimaryKey(TradeMqConsumerLogKey key);

    int updateByExampleSelective(@Param("record") TradeMqConsumerLog record, @Param("example") TradeMqConsumerLogExample example);

    int updateByExample(@Param("record") TradeMqConsumerLog record, @Param("example") TradeMqConsumerLogExample example);

    int updateByPrimaryKeySelective(TradeMqConsumerLog record);

    int updateByPrimaryKey(TradeMqConsumerLog record);
}
5.5、 创建 类 CancelMQListener.java
package com.itheima.shop.mq;

import com.alibaba.fastjson.JSON;
import com.itheima.constant.ShopCode;
import com.itheima.entity.MQEntity;
import com.itheima.shop.mapper.TradeGoodsMapper;
import com.itheima.shop.mapper.TradeGoodsNumberLogMapper;
import com.itheima.shop.mapper.TradeMqConsumerLogMapper;
import com.itheima.shop.pojo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Date;


@Slf4j
@Component
@RocketMQMessageListener(topic = "${mq.order.topic}",consumerGroup = "${mq.order.consumer.group.name}",messageModel = MessageModel.BROADCASTING )
public class CancelMQListener implements RocketMQListener<MessageExt>{


    @Value("${mq.order.consumer.group.name}")
    private String groupName;

    @Autowired
    private TradeGoodsMapper goodsMapper;

    @Autowired
    private TradeMqConsumerLogMapper mqConsumerLogMapper;

    @Autowired
    private TradeGoodsNumberLogMapper goodsNumberLogMapper;

    @Override
    public void onMessage(MessageExt messageExt) {
        String msgId=null;
        String tags=null;
        String keys=null;
        String body=null;
        try {
            //1. 解析消息内容
            msgId = messageExt.getMsgId();
            tags= messageExt.getTags();
            keys= messageExt.getKeys();
            body= new String(messageExt.getBody(),"UTF-8");

            log.info("接受消息成功");

            //2. 查询消息消费记录
            TradeMqConsumerLogKey primaryKey = new TradeMqConsumerLogKey();
            primaryKey.setMsgTag(tags);
            primaryKey.setMsgKey(keys);
            primaryKey.setGroupName(groupName);
            TradeMqConsumerLog mqConsumerLog = mqConsumerLogMapper.selectByPrimaryKey(primaryKey);

            if(mqConsumerLog!=null){
                //3. 判断如果消费过...
                //3.1 获得消息处理状态
                Integer status = mqConsumerLog.getConsumerStatus();
                //处理过...返回
                if(ShopCode.SHOP_MQ_MESSAGE_STATUS_SUCCESS.getCode().intValue()==status.intValue()){
                    log.info("消息:"+msgId+",已经处理过");
                    return;
                }

                //正在处理...返回
                if(ShopCode.SHOP_MQ_MESSAGE_STATUS_PROCESSING.getCode().intValue()==status.intValue()){
                    log.info("消息:"+msgId+",正在处理");
                    return;
                }

                //处理失败
                if(ShopCode.SHOP_MQ_MESSAGE_STATUS_FAIL.getCode().intValue()==status.intValue()){
                    //获得消息处理次数
                    Integer times = mqConsumerLog.getConsumerTimes();
                    if(times>3){
                        log.info("消息:"+msgId+",消息处理超过3次,不能再进行处理了");
                        return;
                    }
                    mqConsumerLog.setConsumerStatus(ShopCode.SHOP_MQ_MESSAGE_STATUS_PROCESSING.getCode());

                    //使用数据库乐观锁更新
                    TradeMqConsumerLogExample example = new TradeMqConsumerLogExample();
                    TradeMqConsumerLogExample.Criteria criteria = example.createCriteria();
                    criteria.andMsgTagEqualTo(mqConsumerLog.getMsgTag());
                    criteria.andMsgKeyEqualTo(mqConsumerLog.getMsgKey());
                    criteria.andGroupNameEqualTo(groupName);
                    criteria.andConsumerTimesEqualTo(mqConsumerLog.getConsumerTimes());
                    int r = mqConsumerLogMapper.updateByExampleSelective(mqConsumerLog, example);
                    if(r<=0){
                        //未修改成功,其他线程并发修改
                        log.info("并发修改,稍后处理");
                    }
                }

            }else{
                //4. 判断如果没有消费过...
                mqConsumerLog = new TradeMqConsumerLog();
                mqConsumerLog.setMsgTag(tags);
                mqConsumerLog.setMsgKey(keys);
                mqConsumerLog.setGroupName(groupName);
                mqConsumerLog.setConsumerStatus(ShopCode.SHOP_MQ_MESSAGE_STATUS_PROCESSING.getCode());
                mqConsumerLog.setMsgBody(body);
                mqConsumerLog.setMsgId(msgId);
                mqConsumerLog.setConsumerTimes(0);

                //将消息处理信息添加到数据库
                mqConsumerLogMapper.insert(mqConsumerLog);
            }
            //5. 回退库存
            MQEntity mqEntity = JSON.parseObject(body, MQEntity.class);
            Long goodsId = mqEntity.getGoodsId();
            TradeGoods goods = goodsMapper.selectByPrimaryKey(goodsId);
            goods.setGoodsNumber(goods.getGoodsNumber()+mqEntity.getGoodsNum());
            goodsMapper.updateByPrimaryKey(goods);


            //6. 将消息的处理状态改为成功
            mqConsumerLog.setConsumerStatus(ShopCode.SHOP_MQ_MESSAGE_STATUS_SUCCESS.getCode());
            mqConsumerLog.setConsumerTimestamp(new Date());
            mqConsumerLogMapper.updateByPrimaryKey(mqConsumerLog);
            log.info("回退库存成功");
        } catch (Exception e) {
            e.printStackTrace();
            TradeMqConsumerLogKey primaryKey = new TradeMqConsumerLogKey();
            primaryKey.setMsgTag(tags);
            primaryKey.setMsgKey(keys);
            primaryKey.setGroupName(groupName);
            TradeMqConsumerLog mqConsumerLog = mqConsumerLogMapper.selectByPrimaryKey(primaryKey);
            if(mqConsumerLog==null){
                //数据库未有记录
                mqConsumerLog = new TradeMqConsumerLog();
                mqConsumerLog.setMsgTag(tags);
                mqConsumerLog.setMsgKey(keys);
                mqConsumerLog.setGroupName(groupName);
                mqConsumerLog.setConsumerStatus(ShopCode.SHOP_MQ_MESSAGE_STATUS_FAIL.getCode());
                mqConsumerLog.setMsgBody(body);
                mqConsumerLog.setMsgId(msgId);
                mqConsumerLog.setConsumerTimes(1);
                mqConsumerLogMapper.insert(mqConsumerLog);
            }else{
                mqConsumerLog.setConsumerTimes(mqConsumerLog.getConsumerTimes()+1);
                mqConsumerLogMapper.updateByPrimaryKeySelective(mqConsumerLog);
            }
        }

    }
}

5.6、 创建 类 GoodsServiceImpl.java
package com.itheima.shop.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.itheima.api.IGoodsService;
import com.itheima.constant.ShopCode;
import com.itheima.entity.Result;
import com.itheima.exception.CastException;
import com.itheima.shop.mapper.TradeGoodsMapper;
import com.itheima.shop.mapper.TradeGoodsNumberLogMapper;
import com.itheima.shop.pojo.TradeGoods;
import com.itheima.shop.pojo.TradeGoodsNumberLog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
@Service(interfaceClass = IGoodsService.class)
public class GoodsServiceImpl implements IGoodsService {

    @Autowired
    private TradeGoodsMapper goodsMapper;

    @Autowired
    private TradeGoodsNumberLogMapper goodsNumberLogMapper;

    @Override
    public TradeGoods findOne(Long goodsId) {
        if (goodsId == null) {
            CastException.cast(ShopCode.SHOP_REQUEST_PARAMETER_VALID);
        }
        return goodsMapper.selectByPrimaryKey(goodsId);
    }

    @Override
    public Result reduceGoodsNum(TradeGoodsNumberLog goodsNumberLog) {
        if (goodsNumberLog == null ||
                goodsNumberLog.getGoodsNumber() == null ||
                goodsNumberLog.getOrderId() == null ||
                goodsNumberLog.getGoodsNumber() == null ||
                goodsNumberLog.getGoodsNumber().intValue() <= 0) {
            CastException.cast(ShopCode.SHOP_REQUEST_PARAMETER_VALID);
        }
        TradeGoods goods = goodsMapper.selectByPrimaryKey(goodsNumberLog.getGoodsId());
        if(goods.getGoodsNumber()<goodsNumberLog.getGoodsNumber()){
            //库存不足
            CastException.cast(ShopCode.SHOP_GOODS_NUM_NOT_ENOUGH);
        }
        //减库存
        goods.setGoodsNumber(goods.getGoodsNumber()-goodsNumberLog.getGoodsNumber());
        goodsMapper.updateByPrimaryKey(goods);


        //记录库存操作日志
        goodsNumberLog.setGoodsNumber(-(goodsNumberLog.getGoodsNumber()));
        goodsNumberLog.setLogTime(new Date());
        goodsNumberLogMapper.insert(goodsNumberLog);

        return new Result(ShopCode.SHOP_SUCCESS.getSuccess(),ShopCode.SHOP_SUCCESS.getMessage());
    }



}

5.7、 创建 类 GoodsServiceApplication.java
package com.itheima.shop;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubboConfiguration
public class GoodsServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(GoodsServiceApplication.class,args);
    }

}

5.8、 创建 文件 TradeGoodsMapper.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.itheima.shop.mapper.TradeGoodsMapper" >
  <resultMap id="BaseResultMap" type="com.itheima.shop.pojo.TradeGoods" >
    <id column="goods_id" property="goodsId" jdbcType="BIGINT" />
    <result column="goods_name" property="goodsName" jdbcType="VARCHAR" />
    <result column="goods_number" property="goodsNumber" jdbcType="INTEGER" />
    <result column="goods_price" property="goodsPrice" jdbcType="DECIMAL" />
    <result column="goods_desc" property="goodsDesc" jdbcType="VARCHAR" />
    <result column="add_time" property="addTime" jdbcType="TIMESTAMP" />
  </resultMap>
  <sql id="Example_Where_Clause" >
    <where >
      <foreach collection="oredCriteria" item="criteria" separator="or" >
        <if test="criteria.valid" >
          <trim prefix="(" suffix=")" prefixOverrides="and" >
            <foreach collection="criteria.criteria" item="criterion" >
              <choose >
                <when test="criterion.noValue" >
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue" >
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue" >
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue" >
                  and ${criterion.condition}
                  <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Update_By_Example_Where_Clause" >
    <where >
      <foreach collection="example.oredCriteria" item="criteria" separator="or" >
        <if test="criteria.valid" >
          <trim prefix="(" suffix=")" prefixOverrides="and" >
            <foreach collection="criteria.criteria" item="criterion" >
              <choose >
                <when test="criterion.noValue" >
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue" >
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue" >
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue" >
                  and ${criterion.condition}
                  <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Base_Column_List" >
    goods_id, goods_name, goods_number, goods_price, goods_desc, add_time
  </sql>
  <select id="selectByExample" resultMap="BaseResultMap" parameterType="com.itheima.shop.pojo.TradeGoodsExample" >
    select
    <if test="distinct" >
      distinct
    </if>
    <include refid="Base_Column_List" />
    from trade_goods
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
    <if test="orderByClause != null" >
      order by ${orderByClause}
    </if>
  </select>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select 
    <include refid="Base_Column_List" />
    from trade_goods
    where goods_id = #{goodsId,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
    delete from trade_goods
    where goods_id = #{goodsId,jdbcType=BIGINT}
  </delete>
  <delete id="deleteByExample" parameterType="com.itheima.shop.pojo.TradeGoodsExample" >
    delete from trade_goods
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
  </delete>
  <insert id="insert" parameterType="com.itheima.shop.pojo.TradeGoods" >
    insert into trade_goods (goods_id, goods_name, goods_number, 
      goods_price, goods_desc, add_time
      )
    values (#{goodsId,jdbcType=BIGINT}, #{goodsName,jdbcType=VARCHAR}, #{goodsNumber,jdbcType=INTEGER}, 
      #{goodsPrice,jdbcType=DECIMAL}, #{goodsDesc,jdbcType=VARCHAR}, #{addTime,jdbcType=TIMESTAMP}
      )
  </insert>
  <insert id="insertSelective" parameterType="com.itheima.shop.pojo.TradeGoods" >
    insert into trade_goods
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="goodsId != null" >
        goods_id,
      </if>
      <if test="goodsName != null" >
        goods_name,
      </if>
      <if test="goodsNumber != null" >
        goods_number,
      </if>
      <if test="goodsPrice != null" >
        goods_price,
      </if>
      <if test="goodsDesc != null" >
        goods_desc,
      </if>
      <if test="addTime != null" >
        add_time,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="goodsId != null" >
        #{goodsId,jdbcType=BIGINT},
      </if>
      <if test="goodsName != null" >
        #{goodsName,jdbcType=VARCHAR},
      </if>
      <if test="goodsNumber != null" >
        #{goodsNumber,jdbcType=INTEGER},
      </if>
      <if test="goodsPrice != null" >
        #{goodsPrice,jdbcType=DECIMAL},
      </if>
      <if test="goodsDesc != null" >
        #{goodsDesc,jdbcType=VARCHAR},
      </if>
      <if test="addTime != null" >
        #{addTime,jdbcType=TIMESTAMP},
      </if>
    </trim>
  </insert>
  <select id="countByExample" parameterType="com.itheima.shop.pojo.TradeGoodsExample" resultType="java.lang.Integer" >
    select count(*) from trade_goods
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
  </select>
  <update id="updateByExampleSelective" parameterType="map" >
    update trade_goods
    <set >
      <if test="record.goodsId != null" >
        goods_id = #{record.goodsId,jdbcType=BIGINT},
      </if>
      <if test="record.goodsName != null" >
        goods_name = #{record.goodsName,jdbcType=VARCHAR},
      </if>
      <if test="record.goodsNumber != null" >
        goods_number = #{record.goodsNumber,jdbcType=INTEGER},
      </if>
      <if test="record.goodsPrice != null" >
        goods_price = #{record.goodsPrice,jdbcType=DECIMAL},
      </if>
      <if test="record.goodsDesc != null" >
        goods_desc = #{record.goodsDesc,jdbcType=VARCHAR},
      </if>
      <if test="record.addTime != null" >
        add_time = #{record.addTime,jdbcType=TIMESTAMP},
      </if>
    </set>
    <if test="_parameter != null" >
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByExample" parameterType="map" >
    update trade_goods
    set goods_id = #{record.goodsId,jdbcType=BIGINT},
      goods_name = #{record.goodsName,jdbcType=VARCHAR},
      goods_number = #{record.goodsNumber,jdbcType=INTEGER},
      goods_price = #{record.goodsPrice,jdbcType=DECIMAL},
      goods_desc = #{record.goodsDesc,jdbcType=VARCHAR},
      add_time = #{record.addTime,jdbcType=TIMESTAMP}
    <if test="_parameter != null" >
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByPrimaryKeySelective" parameterType="com.itheima.shop.pojo.TradeGoods" >
    update trade_goods
    <set >
      <if test="goodsName != null" >
        goods_name = #{goodsName,jdbcType=VARCHAR},
      </if>
      <if test="goodsNumber != null" >
        goods_number = #{goodsNumber,jdbcType=INTEGER},
      </if>
      <if test="goodsPrice != null" >
        goods_price = #{goodsPrice,jdbcType=DECIMAL},
      </if>
      <if test="goodsDesc != null" >
        goods_desc = #{goodsDesc,jdbcType=VARCHAR},
      </if>
      <if test="addTime != null" >
        add_time = #{addTime,jdbcType=TIMESTAMP},
      </if>
    </set>
    where goods_id = #{goodsId,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.itheima.shop.pojo.TradeGoods" >
    update trade_goods
    set goods_name = #{goodsName,jdbcType=VARCHAR},
      goods_number = #{goodsNumber,jdbcType=INTEGER},
      goods_price = #{goodsPrice,jdbcType=DECIMAL},
      goods_desc = #{goodsDesc,jdbcType=VARCHAR},
      add_time = #{addTime,jdbcType=TIMESTAMP}
    where goods_id = #{goodsId,jdbcType=BIGINT}
  </update>
</mapper>
5.9、 创建 文件 TradeGoodsNumberLogMapper.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.itheima.shop.mapper.TradeGoodsNumberLogMapper" >
  <resultMap id="BaseResultMap" type="com.itheima.shop.pojo.TradeGoodsNumberLog" >
    <id column="goods_id" property="goodsId" jdbcType="BIGINT" />
    <id column="order_id" property="orderId" jdbcType="BIGINT" />
    <result column="goods_number" property="goodsNumber" jdbcType="INTEGER" />
    <result column="log_time" property="logTime" jdbcType="TIMESTAMP" />
  </resultMap>
  <sql id="Example_Where_Clause" >
    <where >
      <foreach collection="oredCriteria" item="criteria" separator="or" >
        <if test="criteria.valid" >
          <trim prefix="(" suffix=")" prefixOverrides="and" >
            <foreach collection="criteria.criteria" item="criterion" >
              <choose >
                <when test="criterion.noValue" >
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue" >
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue" >
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue" >
                  and ${criterion.condition}
                  <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Update_By_Example_Where_Clause" >
    <where >
      <foreach collection="example.oredCriteria" item="criteria" separator="or" >
        <if test="criteria.valid" >
          <trim prefix="(" suffix=")" prefixOverrides="and" >
            <foreach collection="criteria.criteria" item="criterion" >
              <choose >
                <when test="criterion.noValue" >
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue" >
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue" >
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue" >
                  and ${criterion.condition}
                  <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Base_Column_List" >
    goods_id, order_id, goods_number, log_time
  </sql>
  <select id="selectByExample" resultMap="BaseResultMap" parameterType="com.itheima.shop.pojo.TradeGoodsNumberLogExample" >
    select
    <if test="distinct" >
      distinct
    </if>
    <include refid="Base_Column_List" />
    from trade_goods_number_log
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
    <if test="orderByClause != null" >
      order by ${orderByClause}
    </if>
  </select>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="com.itheima.shop.pojo.TradeGoodsNumberLogKey" >
    select 
    <include refid="Base_Column_List" />
    from trade_goods_number_log
    where goods_id = #{goodsId,jdbcType=BIGINT}
      and order_id = #{orderId,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="com.itheima.shop.pojo.TradeGoodsNumberLogKey" >
    delete from trade_goods_number_log
    where goods_id = #{goodsId,jdbcType=BIGINT}
      and order_id = #{orderId,jdbcType=BIGINT}
  </delete>
  <delete id="deleteByExample" parameterType="com.itheima.shop.pojo.TradeGoodsNumberLogExample" >
    delete from trade_goods_number_log
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
  </delete>
  <insert id="insert" parameterType="com.itheima.shop.pojo.TradeGoodsNumberLog" >
    insert into trade_goods_number_log (goods_id, order_id, goods_number, 
      log_time)
    values (#{goodsId,jdbcType=BIGINT}, #{orderId,jdbcType=BIGINT}, #{goodsNumber,jdbcType=INTEGER}, 
      #{logTime,jdbcType=TIMESTAMP})
  </insert>
  <insert id="insertSelective" parameterType="com.itheima.shop.pojo.TradeGoodsNumberLog" >
    insert into trade_goods_number_log
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="goodsId != null" >
        goods_id,
      </if>
      <if test="orderId != null" >
        order_id,
      </if>
      <if test="goodsNumber != null" >
        goods_number,
      </if>
      <if test="logTime != null" >
        log_time,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="goodsId != null" >
        #{goodsId,jdbcType=BIGINT},
      </if>
      <if test="orderId != null" >
        #{orderId,jdbcType=BIGINT},
      </if>
      <if test="goodsNumber != null" >
        #{goodsNumber,jdbcType=INTEGER},
      </if>
      <if test="logTime != null" >
        #{logTime,jdbcType=TIMESTAMP},
      </if>
    </trim>
  </insert>
  <select id="countByExample" parameterType="com.itheima.shop.pojo.TradeGoodsNumberLogExample" resultType="java.lang.Integer" >
    select count(*) from trade_goods_number_log
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
  </select>
  <update id="updateByExampleSelective" parameterType="map" >
    update trade_goods_number_log
    <set >
      <if test="record.goodsId != null" >
        goods_id = #{record.goodsId,jdbcType=BIGINT},
      </if>
      <if test="record.orderId != null" >
        order_id = #{record.orderId,jdbcType=BIGINT},
      </if>
      <if test="record.goodsNumber != null" >
        goods_number = #{record.goodsNumber,jdbcType=INTEGER},
      </if>
      <if test="record.logTime != null" >
        log_time = #{record.logTime,jdbcType=TIMESTAMP},
      </if>
    </set>
    <if test="_parameter != null" >
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByExample" parameterType="map" >
    update trade_goods_number_log
    set goods_id = #{record.goodsId,jdbcType=BIGINT},
      order_id = #{record.orderId,jdbcType=BIGINT},
      goods_number = #{record.goodsNumber,jdbcType=INTEGER},
      log_time = #{record.logTime,jdbcType=TIMESTAMP}
    <if test="_parameter != null" >
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByPrimaryKeySelective" parameterType="com.itheima.shop.pojo.TradeGoodsNumberLog" >
    update trade_goods_number_log
    <set >
      <if test="goodsNumber != null" >
        goods_number = #{goodsNumber,jdbcType=INTEGER},
      </if>
      <if test="logTime != null" >
        log_time = #{logTime,jdbcType=TIMESTAMP},
      </if>
    </set>
    where goods_id = #{goodsId,jdbcType=BIGINT}
      and order_id = #{orderId,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.itheima.shop.pojo.TradeGoodsNumberLog" >
    update trade_goods_number_log
    set goods_number = #{goodsNumber,jdbcType=INTEGER},
      log_time = #{logTime,jdbcType=TIMESTAMP}
    where goods_id = #{goodsId,jdbcType=BIGINT}
      and order_id = #{orderId,jdbcType=BIGINT}
  </update>
</mapper>
5.10、 创建 文件 TradeMqConsumerLogMapper.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.itheima.shop.mapper.TradeMqConsumerLogMapper" >
  <resultMap id="BaseResultMap" type="com.itheima.shop.pojo.TradeMqConsumerLog" >
    <id column="group_name" property="groupName" jdbcType="VARCHAR" />
    <id column="msg_tag" property="msgTag" jdbcType="VARCHAR" />
    <id column="msg_key" property="msgKey" jdbcType="VARCHAR" />
    <result column="msg_id" property="msgId" jdbcType="VARCHAR" />
    <result column="msg_body" property="msgBody" jdbcType="VARCHAR" />
    <result column="consumer_status" property="consumerStatus" jdbcType="INTEGER" />
    <result column="consumer_times" property="consumerTimes" jdbcType="INTEGER" />
    <result column="consumer_timestamp" property="consumerTimestamp" jdbcType="TIMESTAMP" />
    <result column="remark" property="remark" jdbcType="VARCHAR" />
  </resultMap>
  <sql id="Example_Where_Clause" >
    <where >
      <foreach collection="oredCriteria" item="criteria" separator="or" >
        <if test="criteria.valid" >
          <trim prefix="(" suffix=")" prefixOverrides="and" >
            <foreach collection="criteria.criteria" item="criterion" >
              <choose >
                <when test="criterion.noValue" >
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue" >
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue" >
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue" >
                  and ${criterion.condition}
                  <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Update_By_Example_Where_Clause" >
    <where >
      <foreach collection="example.oredCriteria" item="criteria" separator="or" >
        <if test="criteria.valid" >
          <trim prefix="(" suffix=")" prefixOverrides="and" >
            <foreach collection="criteria.criteria" item="criterion" >
              <choose >
                <when test="criterion.noValue" >
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue" >
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue" >
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue" >
                  and ${criterion.condition}
                  <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Base_Column_List" >
    group_name, msg_tag, msg_key, msg_id, msg_body, consumer_status, consumer_times, 
    consumer_timestamp, remark
  </sql>
  <select id="selectByExample" resultMap="BaseResultMap" parameterType="com.itheima.shop.pojo.TradeMqConsumerLogExample" >
    select
    <if test="distinct" >
      distinct
    </if>
    <include refid="Base_Column_List" />
    from trade_mq_consumer_log
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
    <if test="orderByClause != null" >
      order by ${orderByClause}
    </if>
  </select>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="com.itheima.shop.pojo.TradeMqConsumerLogKey" >
    select 
    <include refid="Base_Column_List" />
    from trade_mq_consumer_log
    where group_name = #{groupName,jdbcType=VARCHAR}
      and msg_tag = #{msgTag,jdbcType=VARCHAR}
      and msg_key = #{msgKey,jdbcType=VARCHAR}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="com.itheima.shop.pojo.TradeMqConsumerLogKey" >
    delete from trade_mq_consumer_log
    where group_name = #{groupName,jdbcType=VARCHAR}
      and msg_tag = #{msgTag,jdbcType=VARCHAR}
      and msg_key = #{msgKey,jdbcType=VARCHAR}
  </delete>
  <delete id="deleteByExample" parameterType="com.itheima.shop.pojo.TradeMqConsumerLogExample" >
    delete from trade_mq_consumer_log
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
  </delete>
  <insert id="insert" parameterType="com.itheima.shop.pojo.TradeMqConsumerLog" >
    insert into trade_mq_consumer_log (group_name, msg_tag, msg_key, 
      msg_id, msg_body, consumer_status, 
      consumer_times, consumer_timestamp, remark
      )
    values (#{groupName,jdbcType=VARCHAR}, #{msgTag,jdbcType=VARCHAR}, #{msgKey,jdbcType=VARCHAR}, 
      #{msgId,jdbcType=VARCHAR}, #{msgBody,jdbcType=VARCHAR}, #{consumerStatus,jdbcType=INTEGER}, 
      #{consumerTimes,jdbcType=INTEGER}, #{consumerTimestamp,jdbcType=TIMESTAMP}, #{remark,jdbcType=VARCHAR}
      )
  </insert>
  <insert id="insertSelective" parameterType="com.itheima.shop.pojo.TradeMqConsumerLog" >
    insert into trade_mq_consumer_log
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="groupName != null" >
        group_name,
      </if>
      <if test="msgTag != null" >
        msg_tag,
      </if>
      <if test="msgKey != null" >
        msg_key,
      </if>
      <if test="msgId != null" >
        msg_id,
      </if>
      <if test="msgBody != null" >
        msg_body,
      </if>
      <if test="consumerStatus != null" >
        consumer_status,
      </if>
      <if test="consumerTimes != null" >
        consumer_times,
      </if>
      <if test="consumerTimestamp != null" >
        consumer_timestamp,
      </if>
      <if test="remark != null" >
        remark,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="groupName != null" >
        #{groupName,jdbcType=VARCHAR},
      </if>
      <if test="msgTag != null" >
        #{msgTag,jdbcType=VARCHAR},
      </if>
      <if test="msgKey != null" >
        #{msgKey,jdbcType=VARCHAR},
      </if>
      <if test="msgId != null" >
        #{msgId,jdbcType=VARCHAR},
      </if>
      <if test="msgBody != null" >
        #{msgBody,jdbcType=VARCHAR},
      </if>
      <if test="consumerStatus != null" >
        #{consumerStatus,jdbcType=INTEGER},
      </if>
      <if test="consumerTimes != null" >
        #{consumerTimes,jdbcType=INTEGER},
      </if>
      <if test="consumerTimestamp != null" >
        #{consumerTimestamp,jdbcType=TIMESTAMP},
      </if>
      <if test="remark != null" >
        #{remark,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <select id="countByExample" parameterType="com.itheima.shop.pojo.TradeMqConsumerLogExample" resultType="java.lang.Integer" >
    select count(*) from trade_mq_consumer_log
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
  </select>
  <update id="updateByExampleSelective" parameterType="map" >
    update trade_mq_consumer_log
    <set >
      <if test="record.groupName != null" >
        group_name = #{record.groupName,jdbcType=VARCHAR},
      </if>
      <if test="record.msgTag != null" >
        msg_tag = #{record.msgTag,jdbcType=VARCHAR},
      </if>
      <if test="record.msgKey != null" >
        msg_key = #{record.msgKey,jdbcType=VARCHAR},
      </if>
      <if test="record.msgId != null" >
        msg_id = #{record.msgId,jdbcType=VARCHAR},
      </if>
      <if test="record.msgBody != null" >
        msg_body = #{record.msgBody,jdbcType=VARCHAR},
      </if>
      <if test="record.consumerStatus != null" >
        consumer_status = #{record.consumerStatus,jdbcType=INTEGER},
      </if>
      <if test="record.consumerTimes != null" >
        consumer_times = #{record.consumerTimes,jdbcType=INTEGER},
      </if>
      <if test="record.consumerTimestamp != null" >
        consumer_timestamp = #{record.consumerTimestamp,jdbcType=TIMESTAMP},
      </if>
      <if test="record.remark != null" >
        remark = #{record.remark,jdbcType=VARCHAR},
      </if>
    </set>
    <if test="_parameter != null" >
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByExample" parameterType="map" >
    update trade_mq_consumer_log
    set group_name = #{record.groupName,jdbcType=VARCHAR},
      msg_tag = #{record.msgTag,jdbcType=VARCHAR},
      msg_key = #{record.msgKey,jdbcType=VARCHAR},
      msg_id = #{record.msgId,jdbcType=VARCHAR},
      msg_body = #{record.msgBody,jdbcType=VARCHAR},
      consumer_status = #{record.consumerStatus,jdbcType=INTEGER},
      consumer_times = #{record.consumerTimes,jdbcType=INTEGER},
      consumer_timestamp = #{record.consumerTimestamp,jdbcType=TIMESTAMP},
      remark = #{record.remark,jdbcType=VARCHAR}
    <if test="_parameter != null" >
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByPrimaryKeySelective" parameterType="com.itheima.shop.pojo.TradeMqConsumerLog" >
    update trade_mq_consumer_log
    <set >
      <if test="msgId != null" >
        msg_id = #{msgId,jdbcType=VARCHAR},
      </if>
      <if test="msgBody != null" >
        msg_body = #{msgBody,jdbcType=VARCHAR},
      </if>
      <if test="consumerStatus != null" >
        consumer_status = #{consumerStatus,jdbcType=INTEGER},
      </if>
      <if test="consumerTimes != null" >
        consumer_times = #{consumerTimes,jdbcType=INTEGER},
      </if>
      <if test="consumerTimestamp != null" >
        consumer_timestamp = #{consumerTimestamp,jdbcType=TIMESTAMP},
      </if>
      <if test="remark != null" >
        remark = #{remark,jdbcType=VARCHAR},
      </if>
    </set>
    where group_name = #{groupName,jdbcType=VARCHAR}
      and msg_tag = #{msgTag,jdbcType=VARCHAR}
      and msg_key = #{msgKey,jdbcType=VARCHAR}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.itheima.shop.pojo.TradeMqConsumerLog" >
    update trade_mq_consumer_log
    set msg_id = #{msgId,jdbcType=VARCHAR},
      msg_body = #{msgBody,jdbcType=VARCHAR},
      consumer_status = #{consumerStatus,jdbcType=INTEGER},
      consumer_times = #{consumerTimes,jdbcType=INTEGER},
      consumer_timestamp = #{consumerTimestamp,jdbcType=TIMESTAMP},
      remark = #{remark,jdbcType=VARCHAR}
    where group_name = #{groupName,jdbcType=VARCHAR}
      and msg_tag = #{msgTag,jdbcType=VARCHAR}
      and msg_key = #{msgKey,jdbcType=VARCHAR}
  </update>
</mapper>
5.11、 创建 文件 application.properties
spring.application.name=dubbo-goods-provider
spring.dubbo.application.id=dubbo-goods-provider
spring.dubbo.application.name=dubbo-goods-provider
spring.dubbo.registry.address=zookeeper://192.168.25.140:2181;zookeeper://192.168.25.140:2182;zookeeper://192.168.25.140:2183
spring.dubbo.server=true
spring.dubbo.protocol.name=dubbo
spring.dubbo.protocol.port=20882

# DB
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/trade?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root

#spring集成Mybatis环境
#pojo别名扫描包
mybatis.type-aliases-package=com.itheima.shop.pojo
#加载Mybatis映射文件
mybatis.mapper-locations=classpath:com/itheima/shop/mapper/*Mapper.xml


# RocketMQ
rocketmq.name-server=192.168.25.135:9876;192.168.25.138:9876
rocketmq.producer.group=orderProducerGroup

mq.order.consumer.group.name=order_orderTopic_cancel_group
mq.order.topic=orderTopic

6、创建 shop-order-service 订单服务 工程。

6.1、在 pom.xml 中,导入依赖坐标。

<?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>shop-parent</artifactId>
        <groupId>com.iteima.shop</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../shop-parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>shop-order-service</artifactId>

    <dependencies>

        <!--mybatis-springboot-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>

        <!-- MySQL连接驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!--common-->
        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--接口-->
        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-pojo</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

    </dependencies>

</project>
<!-- D:\java-test\idea2019\shop\shop-order-service\pom.xml -->

6.2、创建 接口 类 TradeOrderMapper.java
package com.itheima.shop.mapper;

import com.itheima.shop.pojo.TradeOrder;
import com.itheima.shop.pojo.TradeOrderExample;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface TradeOrderMapper {
    int countByExample(TradeOrderExample example);

    int deleteByExample(TradeOrderExample example);

    int deleteByPrimaryKey(Long orderId);

    int insert(TradeOrder record);

    int insertSelective(TradeOrder record);

    List<TradeOrder> selectByExample(TradeOrderExample example);

    TradeOrder selectByPrimaryKey(Long orderId);

    int updateByExampleSelective(@Param("record") TradeOrder record, @Param("example") TradeOrderExample example);

    int updateByExample(@Param("record") TradeOrder record, @Param("example") TradeOrderExample example);

    int updateByPrimaryKeySelective(TradeOrder record);

    int updateByPrimaryKey(TradeOrder record);
}
6.3、创建 类 CancelMQListener.java
package com.itheima.shop.mq;

import com.alibaba.fastjson.JSON;
import com.itheima.constant.ShopCode;
import com.itheima.entity.MQEntity;
import com.itheima.shop.mapper.TradeOrderMapper;
import com.itheima.shop.pojo.TradeOrder;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;


@Slf4j
@Component
@RocketMQMessageListener(topic = "${mq.order.topic}",consumerGroup = "${mq.order.consumer.group.name}",messageModel = MessageModel.BROADCASTING )
public class CancelMQListener implements RocketMQListener<MessageExt>{

    @Autowired
    private TradeOrderMapper orderMapper;

    @Override
    public void onMessage(MessageExt messageExt) {

        try {
            //1. 解析消息内容
            String body = new String(messageExt.getBody(),"UTF-8");
            MQEntity mqEntity = JSON.parseObject(body, MQEntity.class);
            log.info("接受消息成功");
            //2. 查询订单
            TradeOrder order = orderMapper.selectByPrimaryKey(mqEntity.getOrderId());
            //3.更新订单状态为取消
            order.setOrderStatus(ShopCode.SHOP_ORDER_CANCEL.getCode());
            orderMapper.updateByPrimaryKey(order);
            log.info("订单状态设置为取消");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            log.info("订单取消失败");
        }
    }
}

6.4、创建 类 PaymentListener.java
package com.itheima.shop.mq;

import com.alibaba.fastjson.JSON;
import com.itheima.constant.ShopCode;
import com.itheima.shop.mapper.TradeOrderMapper;
import com.itheima.shop.pojo.TradeOrder;
import com.itheima.shop.pojo.TradePay;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;


@Slf4j
@Component
@RocketMQMessageListener(topic = "${mq.pay.topic}",consumerGroup = "${mq.pay.consumer.group.name}",messageModel = MessageModel.BROADCASTING)
public class PaymentListener implements RocketMQListener<MessageExt>{

    @Autowired
    private TradeOrderMapper orderMapper;

    @Override
    public void onMessage(MessageExt messageExt) {

        log.info("接收到支付成功消息");

        try {
            //1.解析消息内容
            String body = new String(messageExt.getBody(),"UTF-8");
            TradePay tradePay = JSON.parseObject(body,TradePay.class);
            //2.根据订单ID查询订单对象
            TradeOrder tradeOrder = orderMapper.selectByPrimaryKey(tradePay.getOrderId());
            //3.更改订单支付状态为已支付
            tradeOrder.setPayStatus(ShopCode.SHOP_ORDER_PAY_STATUS_IS_PAY.getCode());
            //4.更新订单数据到数据库
            orderMapper.updateByPrimaryKey(tradeOrder);
            log.info("更改订单支付状态为已支付");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

    }
}


6.5、创建 类 OrderServiceImpl.java
package com.itheima.shop.service;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.itheima.api.ICouponService;
import com.itheima.api.IGoodsService;
import com.itheima.api.IOrderService;
import com.itheima.api.IUserService;
import com.itheima.constant.ShopCode;
import com.itheima.entity.MQEntity;
import com.itheima.entity.Result;
import com.itheima.exception.CastException;
import com.itheima.shop.mapper.TradeOrderMapper;
import com.itheima.shop.pojo.*;
import com.itheima.utils.IDWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

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

@Slf4j
@Component
@Service(interfaceClass = IOrderService.class)
public class OrderServiceImpl implements IOrderService {


    @Reference
    private IGoodsService goodsService;

    @Reference
    private IUserService userService;

    @Reference
    private ICouponService couponService;


    @Value("${mq.order.topic}")
    private String topic;

    @Value("${mq.order.tag.cancel}")
    private String tag;

    @Autowired
    private TradeOrderMapper orderMapper;

    @Autowired
    private IDWorker idWorker;


    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Override
    public Result confirmOrder(TradeOrder order) {
        //1.校验订单
        checkOrder(order);
        //2.生成预订单
        Long orderId = savePreOrder(order);
        try {
            //3.扣减库存
            reduceGoodsNum(order);
            //4.扣减优惠券
            updateCouponStatus(order);
            //5.使用余额
            reduceMoneyPaid(order);

            //模拟异常抛出
            //CastException.cast(ShopCode.SHOP_FAIL);

            //6.确认订单
            updateOrderStatus(order);
            //7.返回成功状态
            return new Result(ShopCode.SHOP_SUCCESS.getSuccess(),ShopCode.SHOP_SUCCESS.getMessage());
        } catch (Exception e) {
            //1.确认订单失败,发送消息
            MQEntity mqEntity = new MQEntity();
            mqEntity.setOrderId(orderId);
            mqEntity.setUserId(order.getUserId());
            mqEntity.setUserMoney(order.getMoneyPaid());
            mqEntity.setGoodsId(order.getGoodsId());
            mqEntity.setGoodsNum(order.getGoodsNumber());
            mqEntity.setCouponId(order.getCouponId());

            //2.返回订单确认失败消息
            try {
                sendCancelOrder(topic,tag,order.getOrderId().toString(), JSON.toJSONString(mqEntity));
            } catch (Exception e1) {
                e1.printStackTrace();
            }
            return new Result(ShopCode.SHOP_FAIL.getSuccess(),ShopCode.SHOP_FAIL.getMessage());
        }
    }

    /**
     * 发送订单确认失败消息
     * @param topic
     * @param tag
     * @param keys
     * @param body
     */
    private void sendCancelOrder(String topic, String tag, String keys, String body) throws InterruptedException, RemotingException, MQClientException, MQBrokerException {
        Message message = new Message(topic,tag,keys,body.getBytes());
        rocketMQTemplate.getProducer().send(message);
    }

    /**
     * 确认订单
     * @param order
     */
    private void updateOrderStatus(TradeOrder order) {
        order.setOrderStatus(ShopCode.SHOP_ORDER_CONFIRM.getCode());
        order.setPayStatus(ShopCode.SHOP_ORDER_PAY_STATUS_NO_PAY.getCode());
        order.setConfirmTime(new Date());
        int r = orderMapper.updateByPrimaryKey(order);
        if(r<=0){
            CastException.cast(ShopCode.SHOP_ORDER_CONFIRM_FAIL);
        }
        log.info("订单:"+order.getOrderId()+"确认订单成功");
    }

    /**
     * 扣减余额
     * @param order
     */
    private void reduceMoneyPaid(TradeOrder order) {
        if(order.getMoneyPaid()!=null && order.getMoneyPaid().compareTo(BigDecimal.ZERO)==1){
            TradeUserMoneyLog userMoneyLog = new TradeUserMoneyLog();
            userMoneyLog.setOrderId(order.getOrderId());
            userMoneyLog.setUserId(order.getUserId());
            userMoneyLog.setUseMoney(order.getMoneyPaid());
            userMoneyLog.setMoneyLogType(ShopCode.SHOP_USER_MONEY_PAID.getCode());
            Result result = userService.updateMoneyPaid(userMoneyLog);
            if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
                CastException.cast(ShopCode.SHOP_USER_MONEY_REDUCE_FAIL);
            }
            log.info("订单:"+order.getOrderId()+",扣减余额成功");
        }
    }

    /**
     * 使用优惠券
     * @param order
     */
    private void updateCouponStatus(TradeOrder order) {
        if(order.getCouponId()!=null){
            TradeCoupon coupon = couponService.findOne(order.getCouponId());
            coupon.setOrderId(order.getOrderId());
            coupon.setIsUsed(ShopCode.SHOP_COUPON_ISUSED.getCode());
            coupon.setUsedTime(new Date());

            //更新优惠券状态
           Result result =  couponService.updateCouponStatus(coupon);
           if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
               CastException.cast(ShopCode.SHOP_COUPON_USE_FAIL);
           }
           log.info("订单:"+order.getOrderId()+",使用优惠券");
        }
    }


    /**
     * 扣减库存
     * @param order
     */
    private void reduceGoodsNum(TradeOrder order) {
        TradeGoodsNumberLog goodsNumberLog = new TradeGoodsNumberLog();
        goodsNumberLog.setOrderId(order.getOrderId());
        goodsNumberLog.setGoodsId(order.getGoodsId());
        goodsNumberLog.setGoodsNumber(order.getGoodsNumber());
        Result result = goodsService.reduceGoodsNum(goodsNumberLog);
        if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
            CastException.cast(ShopCode.SHOP_REDUCE_GOODS_NUM_FAIL);
        }
        log.info("订单:"+order.getOrderId()+"扣减库存成功");
    }


    /**
     * 生成预订单
     *
     * @param order
     * @return
     */
    private Long savePreOrder(TradeOrder order) {
        //1. 设置订单状态为不可见
        order.setOrderStatus(ShopCode.SHOP_ORDER_NO_CONFIRM.getCode());
        //2. 设置订单ID
        long orderId = idWorker.nextId();
        order.setOrderId(orderId);
        //3. 核算订单运费
        BigDecimal shippingFee = calculateShippingFee(order.getOrderAmount());
        if(order.getShippingFee().compareTo(shippingFee)!=0){
            CastException.cast(ShopCode.SHOP_ORDER_SHIPPINGFEE_INVALID);
        }
        //4. 核算订单总金额是否合法
        BigDecimal orderAmount = order.getGoodsPrice().multiply(new BigDecimal(order.getGoodsNumber()));
        orderAmount.add(shippingFee);
        if(order.getOrderAmount().compareTo(orderAmount)!=0){
            CastException.cast(ShopCode.SHOP_ORDERAMOUNT_INVALID);
        }
        //5.判断用户是否使用余额
        BigDecimal moneyPaid = order.getMoneyPaid();
        if(moneyPaid!=null){
            //5.1 订单中余额是否合法
            int r = moneyPaid.compareTo(BigDecimal.ZERO);

            //余额小于0
            if(r==-1){
                CastException.cast(ShopCode.SHOP_MONEY_PAID_LESS_ZERO);
            }

            //余额大于0
            if(r==1){
                TradeUser user = userService.findOne(order.getUserId());

                if(moneyPaid.compareTo(new BigDecimal(user.getUserMoney()))==1){
                    CastException.cast(ShopCode.SHOP_MONEY_PAID_INVALID);
                }
            }

        }else{
            order.setMoneyPaid(BigDecimal.ZERO);
        }
        //6.判断用户是否使用优惠券
        Long couponId = order.getCouponId();
        if(couponId!=null){
            TradeCoupon coupon = couponService.findOne(couponId);
            //6.1 判断优惠券是否存在
            if(coupon==null){
                CastException.cast(ShopCode.SHOP_COUPON_NO_EXIST);
            }
            //6.2 判断优惠券是否已经被使用
            if(coupon.getIsUsed().intValue()==ShopCode.SHOP_COUPON_ISUSED.getCode().intValue()){
                CastException.cast(ShopCode.SHOP_COUPON_ISUSED);
            }

            order.setCouponPaid(coupon.getCouponPrice());

        }else{
            order.setCouponPaid(BigDecimal.ZERO);
        }
        //7.核算订单支付金额    订单总金额-余额-优惠券金额
        BigDecimal payAmount = order.getOrderAmount().subtract(order.getMoneyPaid()).subtract(order.getCouponPaid());
        order.setPayAmount(payAmount);
        //8.设置下单时间
        order.setAddTime(new Date());
        //9.保存订单到数据库
        orderMapper.insert(order);
        //10.返回订单ID
        return orderId;
    }

    /**
     * 核算运费
     * @param orderAmount
     * @return
     */
    private BigDecimal calculateShippingFee(BigDecimal orderAmount) {
        if(orderAmount.compareTo(new BigDecimal(100))==1){
            return BigDecimal.ZERO;
        }else{
            return new BigDecimal(10);
        }

    }


    /**
     * 校验订单
     *
     * @param order
     */
    private void checkOrder(TradeOrder order) {
        //1.校验订单是否存在
        if (order == null) {
            CastException.cast(ShopCode.SHOP_ORDER_INVALID);
        }
        //2.校验订单中的商品是否存在
        TradeGoods goods = goodsService.findOne(order.getGoodsId());
        if (goods == null) {
            CastException.cast(ShopCode.SHOP_GOODS_NO_EXIST);
        }
        //3.校验下单用户是否存在
        TradeUser user = userService.findOne(order.getUserId());
        if (user == null) {
            CastException.cast(ShopCode.SHOP_USER_NO_EXIST);
        }
        //4.校验商品单价是否合法
        if (order.getGoodsPrice().compareTo(goods.getGoodsPrice()) != 0) {
            CastException.cast(ShopCode.SHOP_GOODS_PRICE_INVALID);
        }
        //5.校验订单商品数量是否合法
        if (order.getGoodsNumber() >= goods.getGoodsNumber()) {
            CastException.cast(ShopCode.SHOP_GOODS_NUM_NOT_ENOUGH);
        }

        log.info("校验订单通过");

    }


}

6.6、创建 类 OrderServiceApplication.java
package com.itheima.shop;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import com.itheima.utils.IDWorker;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@EnableDubboConfiguration
public class OrderServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class,args);
    }

    @Bean
    public IDWorker getBean(){
        return new IDWorker(1,1);
    }

}

6.7、创建 文件 TradeOrderMapper.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.itheima.shop.mapper.TradeOrderMapper" >
  <resultMap id="BaseResultMap" type="com.itheima.shop.pojo.TradeOrder" >
    <id column="order_id" property="orderId" jdbcType="BIGINT" />
    <result column="user_id" property="userId" jdbcType="BIGINT" />
    <result column="order_status" property="orderStatus" jdbcType="INTEGER" />
    <result column="pay_status" property="payStatus" jdbcType="INTEGER" />
    <result column="shipping_status" property="shippingStatus" jdbcType="INTEGER" />
    <result column="address" property="address" jdbcType="VARCHAR" />
    <result column="consignee" property="consignee" jdbcType="VARCHAR" />
    <result column="goods_id" property="goodsId" jdbcType="BIGINT" />
    <result column="goods_number" property="goodsNumber" jdbcType="INTEGER" />
    <result column="goods_price" property="goodsPrice" jdbcType="DECIMAL" />
    <result column="goods_amount" property="goodsAmount" jdbcType="DECIMAL" />
    <result column="shipping_fee" property="shippingFee" jdbcType="DECIMAL" />
    <result column="order_amount" property="orderAmount" jdbcType="DECIMAL" />
    <result column="coupon_id" property="couponId" jdbcType="BIGINT" />
    <result column="coupon_paid" property="couponPaid" jdbcType="DECIMAL" />
    <result column="money_paid" property="moneyPaid" jdbcType="DECIMAL" />
    <result column="pay_amount" property="payAmount" jdbcType="DECIMAL" />
    <result column="add_time" property="addTime" jdbcType="TIMESTAMP" />
    <result column="confirm_time" property="confirmTime" jdbcType="TIMESTAMP" />
    <result column="pay_time" property="payTime" jdbcType="TIMESTAMP" />
  </resultMap>
  <sql id="Example_Where_Clause" >
    <where >
      <foreach collection="oredCriteria" item="criteria" separator="or" >
        <if test="criteria.valid" >
          <trim prefix="(" suffix=")" prefixOverrides="and" >
            <foreach collection="criteria.criteria" item="criterion" >
              <choose >
                <when test="criterion.noValue" >
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue" >
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue" >
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue" >
                  and ${criterion.condition}
                  <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Update_By_Example_Where_Clause" >
    <where >
      <foreach collection="example.oredCriteria" item="criteria" separator="or" >
        <if test="criteria.valid" >
          <trim prefix="(" suffix=")" prefixOverrides="and" >
            <foreach collection="criteria.criteria" item="criterion" >
              <choose >
                <when test="criterion.noValue" >
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue" >
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue" >
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue" >
                  and ${criterion.condition}
                  <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Base_Column_List" >
    order_id, user_id, order_status, pay_status, shipping_status, address, consignee, 
    goods_id, goods_number, goods_price, goods_amount, shipping_fee, order_amount, coupon_id, 
    coupon_paid, money_paid, pay_amount, add_time, confirm_time, pay_time
  </sql>
  <select id="selectByExample" resultMap="BaseResultMap" parameterType="com.itheima.shop.pojo.TradeOrderExample" >
    select
    <if test="distinct" >
      distinct
    </if>
    <include refid="Base_Column_List" />
    from trade_order
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
    <if test="orderByClause != null" >
      order by ${orderByClause}
    </if>
  </select>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select 
    <include refid="Base_Column_List" />
    from trade_order
    where order_id = #{orderId,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
    delete from trade_order
    where order_id = #{orderId,jdbcType=BIGINT}
  </delete>
  <delete id="deleteByExample" parameterType="com.itheima.shop.pojo.TradeOrderExample" >
    delete from trade_order
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
  </delete>
  <insert id="insert" parameterType="com.itheima.shop.pojo.TradeOrder" >
    insert into trade_order (order_id, user_id, order_status, 
      pay_status, shipping_status, address, 
      consignee, goods_id, goods_number, 
      goods_price, goods_amount, shipping_fee, 
      order_amount, coupon_id, coupon_paid, 
      money_paid, pay_amount, add_time, 
      confirm_time, pay_time)
    values (#{orderId,jdbcType=BIGINT}, #{userId,jdbcType=BIGINT}, #{orderStatus,jdbcType=INTEGER}, 
      #{payStatus,jdbcType=INTEGER}, #{shippingStatus,jdbcType=INTEGER}, #{address,jdbcType=VARCHAR}, 
      #{consignee,jdbcType=VARCHAR}, #{goodsId,jdbcType=BIGINT}, #{goodsNumber,jdbcType=INTEGER}, 
      #{goodsPrice,jdbcType=DECIMAL}, #{goodsAmount,jdbcType=DECIMAL}, #{shippingFee,jdbcType=DECIMAL}, 
      #{orderAmount,jdbcType=DECIMAL}, #{couponId,jdbcType=BIGINT}, #{couponPaid,jdbcType=DECIMAL}, 
      #{moneyPaid,jdbcType=DECIMAL}, #{payAmount,jdbcType=DECIMAL}, #{addTime,jdbcType=TIMESTAMP}, 
      #{confirmTime,jdbcType=TIMESTAMP}, #{payTime,jdbcType=TIMESTAMP})
  </insert>
  <insert id="insertSelective" parameterType="com.itheima.shop.pojo.TradeOrder" >
    insert into trade_order
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="orderId != null" >
        order_id,
      </if>
      <if test="userId != null" >
        user_id,
      </if>
      <if test="orderStatus != null" >
        order_status,
      </if>
      <if test="payStatus != null" >
        pay_status,
      </if>
      <if test="shippingStatus != null" >
        shipping_status,
      </if>
      <if test="address != null" >
        address,
      </if>
      <if test="consignee != null" >
        consignee,
      </if>
      <if test="goodsId != null" >
        goods_id,
      </if>
      <if test="goodsNumber != null" >
        goods_number,
      </if>
      <if test="goodsPrice != null" >
        goods_price,
      </if>
      <if test="goodsAmount != null" >
        goods_amount,
      </if>
      <if test="shippingFee != null" >
        shipping_fee,
      </if>
      <if test="orderAmount != null" >
        order_amount,
      </if>
      <if test="couponId != null" >
        coupon_id,
      </if>
      <if test="couponPaid != null" >
        coupon_paid,
      </if>
      <if test="moneyPaid != null" >
        money_paid,
      </if>
      <if test="payAmount != null" >
        pay_amount,
      </if>
      <if test="addTime != null" >
        add_time,
      </if>
      <if test="confirmTime != null" >
        confirm_time,
      </if>
      <if test="payTime != null" >
        pay_time,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="orderId != null" >
        #{orderId,jdbcType=BIGINT},
      </if>
      <if test="userId != null" >
        #{userId,jdbcType=BIGINT},
      </if>
      <if test="orderStatus != null" >
        #{orderStatus,jdbcType=INTEGER},
      </if>
      <if test="payStatus != null" >
        #{payStatus,jdbcType=INTEGER},
      </if>
      <if test="shippingStatus != null" >
        #{shippingStatus,jdbcType=INTEGER},
      </if>
      <if test="address != null" >
        #{address,jdbcType=VARCHAR},
      </if>
      <if test="consignee != null" >
        #{consignee,jdbcType=VARCHAR},
      </if>
      <if test="goodsId != null" >
        #{goodsId,jdbcType=BIGINT},
      </if>
      <if test="goodsNumber != null" >
        #{goodsNumber,jdbcType=INTEGER},
      </if>
      <if test="goodsPrice != null" >
        #{goodsPrice,jdbcType=DECIMAL},
      </if>
      <if test="goodsAmount != null" >
        #{goodsAmount,jdbcType=DECIMAL},
      </if>
      <if test="shippingFee != null" >
        #{shippingFee,jdbcType=DECIMAL},
      </if>
      <if test="orderAmount != null" >
        #{orderAmount,jdbcType=DECIMAL},
      </if>
      <if test="couponId != null" >
        #{couponId,jdbcType=BIGINT},
      </if>
      <if test="couponPaid != null" >
        #{couponPaid,jdbcType=DECIMAL},
      </if>
      <if test="moneyPaid != null" >
        #{moneyPaid,jdbcType=DECIMAL},
      </if>
      <if test="payAmount != null" >
        #{payAmount,jdbcType=DECIMAL},
      </if>
      <if test="addTime != null" >
        #{addTime,jdbcType=TIMESTAMP},
      </if>
      <if test="confirmTime != null" >
        #{confirmTime,jdbcType=TIMESTAMP},
      </if>
      <if test="payTime != null" >
        #{payTime,jdbcType=TIMESTAMP},
      </if>
    </trim>
  </insert>
  <select id="countByExample" parameterType="com.itheima.shop.pojo.TradeOrderExample" resultType="java.lang.Integer" >
    select count(*) from trade_order
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
  </select>
  <update id="updateByExampleSelective" parameterType="map" >
    update trade_order
    <set >
      <if test="record.orderId != null" >
        order_id = #{record.orderId,jdbcType=BIGINT},
      </if>
      <if test="record.userId != null" >
        user_id = #{record.userId,jdbcType=BIGINT},
      </if>
      <if test="record.orderStatus != null" >
        order_status = #{record.orderStatus,jdbcType=INTEGER},
      </if>
      <if test="record.payStatus != null" >
        pay_status = #{record.payStatus,jdbcType=INTEGER},
      </if>
      <if test="record.shippingStatus != null" >
        shipping_status = #{record.shippingStatus,jdbcType=INTEGER},
      </if>
      <if test="record.address != null" >
        address = #{record.address,jdbcType=VARCHAR},
      </if>
      <if test="record.consignee != null" >
        consignee = #{record.consignee,jdbcType=VARCHAR},
      </if>
      <if test="record.goodsId != null" >
        goods_id = #{record.goodsId,jdbcType=BIGINT},
      </if>
      <if test="record.goodsNumber != null" >
        goods_number = #{record.goodsNumber,jdbcType=INTEGER},
      </if>
      <if test="record.goodsPrice != null" >
        goods_price = #{record.goodsPrice,jdbcType=DECIMAL},
      </if>
      <if test="record.goodsAmount != null" >
        goods_amount = #{record.goodsAmount,jdbcType=DECIMAL},
      </if>
      <if test="record.shippingFee != null" >
        shipping_fee = #{record.shippingFee,jdbcType=DECIMAL},
      </if>
      <if test="record.orderAmount != null" >
        order_amount = #{record.orderAmount,jdbcType=DECIMAL},
      </if>
      <if test="record.couponId != null" >
        coupon_id = #{record.couponId,jdbcType=BIGINT},
      </if>
      <if test="record.couponPaid != null" >
        coupon_paid = #{record.couponPaid,jdbcType=DECIMAL},
      </if>
      <if test="record.moneyPaid != null" >
        money_paid = #{record.moneyPaid,jdbcType=DECIMAL},
      </if>
      <if test="record.payAmount != null" >
        pay_amount = #{record.payAmount,jdbcType=DECIMAL},
      </if>
      <if test="record.addTime != null" >
        add_time = #{record.addTime,jdbcType=TIMESTAMP},
      </if>
      <if test="record.confirmTime != null" >
        confirm_time = #{record.confirmTime,jdbcType=TIMESTAMP},
      </if>
      <if test="record.payTime != null" >
        pay_time = #{record.payTime,jdbcType=TIMESTAMP},
      </if>
    </set>
    <if test="_parameter != null" >
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByExample" parameterType="map" >
    update trade_order
    set order_id = #{record.orderId,jdbcType=BIGINT},
      user_id = #{record.userId,jdbcType=BIGINT},
      order_status = #{record.orderStatus,jdbcType=INTEGER},
      pay_status = #{record.payStatus,jdbcType=INTEGER},
      shipping_status = #{record.shippingStatus,jdbcType=INTEGER},
      address = #{record.address,jdbcType=VARCHAR},
      consignee = #{record.consignee,jdbcType=VARCHAR},
      goods_id = #{record.goodsId,jdbcType=BIGINT},
      goods_number = #{record.goodsNumber,jdbcType=INTEGER},
      goods_price = #{record.goodsPrice,jdbcType=DECIMAL},
      goods_amount = #{record.goodsAmount,jdbcType=DECIMAL},
      shipping_fee = #{record.shippingFee,jdbcType=DECIMAL},
      order_amount = #{record.orderAmount,jdbcType=DECIMAL},
      coupon_id = #{record.couponId,jdbcType=BIGINT},
      coupon_paid = #{record.couponPaid,jdbcType=DECIMAL},
      money_paid = #{record.moneyPaid,jdbcType=DECIMAL},
      pay_amount = #{record.payAmount,jdbcType=DECIMAL},
      add_time = #{record.addTime,jdbcType=TIMESTAMP},
      confirm_time = #{record.confirmTime,jdbcType=TIMESTAMP},
      pay_time = #{record.payTime,jdbcType=TIMESTAMP}
    <if test="_parameter != null" >
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByPrimaryKeySelective" parameterType="com.itheima.shop.pojo.TradeOrder" >
    update trade_order
    <set >
      <if test="userId != null" >
        user_id = #{userId,jdbcType=BIGINT},
      </if>
      <if test="orderStatus != null" >
        order_status = #{orderStatus,jdbcType=INTEGER},
      </if>
      <if test="payStatus != null" >
        pay_status = #{payStatus,jdbcType=INTEGER},
      </if>
      <if test="shippingStatus != null" >
        shipping_status = #{shippingStatus,jdbcType=INTEGER},
      </if>
      <if test="address != null" >
        address = #{address,jdbcType=VARCHAR},
      </if>
      <if test="consignee != null" >
        consignee = #{consignee,jdbcType=VARCHAR},
      </if>
      <if test="goodsId != null" >
        goods_id = #{goodsId,jdbcType=BIGINT},
      </if>
      <if test="goodsNumber != null" >
        goods_number = #{goodsNumber,jdbcType=INTEGER},
      </if>
      <if test="goodsPrice != null" >
        goods_price = #{goodsPrice,jdbcType=DECIMAL},
      </if>
      <if test="goodsAmount != null" >
        goods_amount = #{goodsAmount,jdbcType=DECIMAL},
      </if>
      <if test="shippingFee != null" >
        shipping_fee = #{shippingFee,jdbcType=DECIMAL},
      </if>
      <if test="orderAmount != null" >
        order_amount = #{orderAmount,jdbcType=DECIMAL},
      </if>
      <if test="couponId != null" >
        coupon_id = #{couponId,jdbcType=BIGINT},
      </if>
      <if test="couponPaid != null" >
        coupon_paid = #{couponPaid,jdbcType=DECIMAL},
      </if>
      <if test="moneyPaid != null" >
        money_paid = #{moneyPaid,jdbcType=DECIMAL},
      </if>
      <if test="payAmount != null" >
        pay_amount = #{payAmount,jdbcType=DECIMAL},
      </if>
      <if test="addTime != null" >
        add_time = #{addTime,jdbcType=TIMESTAMP},
      </if>
      <if test="confirmTime != null" >
        confirm_time = #{confirmTime,jdbcType=TIMESTAMP},
      </if>
      <if test="payTime != null" >
        pay_time = #{payTime,jdbcType=TIMESTAMP},
      </if>
    </set>
    where order_id = #{orderId,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.itheima.shop.pojo.TradeOrder" >
    update trade_order
    set user_id = #{userId,jdbcType=BIGINT},
      order_status = #{orderStatus,jdbcType=INTEGER},
      pay_status = #{payStatus,jdbcType=INTEGER},
      shipping_status = #{shippingStatus,jdbcType=INTEGER},
      address = #{address,jdbcType=VARCHAR},
      consignee = #{consignee,jdbcType=VARCHAR},
      goods_id = #{goodsId,jdbcType=BIGINT},
      goods_number = #{goodsNumber,jdbcType=INTEGER},
      goods_price = #{goodsPrice,jdbcType=DECIMAL},
      goods_amount = #{goodsAmount,jdbcType=DECIMAL},
      shipping_fee = #{shippingFee,jdbcType=DECIMAL},
      order_amount = #{orderAmount,jdbcType=DECIMAL},
      coupon_id = #{couponId,jdbcType=BIGINT},
      coupon_paid = #{couponPaid,jdbcType=DECIMAL},
      money_paid = #{moneyPaid,jdbcType=DECIMAL},
      pay_amount = #{payAmount,jdbcType=DECIMAL},
      add_time = #{addTime,jdbcType=TIMESTAMP},
      confirm_time = #{confirmTime,jdbcType=TIMESTAMP},
      pay_time = #{payTime,jdbcType=TIMESTAMP}
    where order_id = #{orderId,jdbcType=BIGINT}
  </update>
</mapper>
6.8、创建 文件 application.properties
# dubbo
spring.application.name=dubbo-order-provider
spring.dubbo.application.id=dubbo-order-provider
spring.dubbo.application.name=dubbo-order-provider
spring.dubbo.registry.address=zookeeper://192.168.25.140:2181;zookeeper://192.168.25.140:2182;zookeeper://192.168.25.140:2183
spring.dubbo.server=true
spring.dubbo.protocol.name=dubbo
spring.dubbo.protocol.port=20884


# DB
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/trade?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root

# Mybatis
#pojo别名扫描包
mybatis.type-aliases-package=com.itheima.shop.pojo
#加载Mybatis映射文件
mybatis.mapper-locations=classpath:com/itheima/shop/mapper/*Mapper.xml

# RocketMQ
# 下单失败消息发送组
rocketmq.name-server=192.168.25.135:9876;192.168.25.138:9876
rocketmq.producer.group=orderProducerGroup


mq.order.consumer.group.name=order_orderTopic_cancel_group
mq.order.topic=orderTopic
mq.order.tag.cancel=order_cancel

mq.pay.topic=payTopic
mq.pay.consumer.group.name=pay_payTopic_group



6.9 创建 测试类 OrderServiceTest.java
package com.itheima.test;

import com.itheima.api.IOrderService;
import com.itheima.shop.OrderServiceApplication;
import com.itheima.shop.pojo.TradeOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.IOException;
import java.math.BigDecimal;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = OrderServiceApplication.class)
public class OrderServiceTest {

    @Autowired
    private IOrderService orderService;

    @Test
    public void confirmOrder() throws IOException {

        Long coupouId = 345988230098857984L;
        Long goodsId = 345959443973935104L;
        Long userId = 345963634385633280L;

        TradeOrder order = new TradeOrder();
        order.setGoodsId(goodsId);
        order.setUserId(userId);
        order.setCouponId(coupouId);
        order.setAddress("北京");
        order.setGoodsNumber(1);
        order.setGoodsPrice(new BigDecimal(1000));
        order.setShippingFee(BigDecimal.ZERO);
        order.setOrderAmount(new BigDecimal(1000));
        order.setMoneyPaid(new BigDecimal(100));
        orderService.confirmOrder(order);

        System.in.read();

    }

}

7、创建 shop-pay-service 支付服务 工程。

7.1、在 pom.xml 中,导入依赖坐标。

<?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>shop-parent</artifactId>
        <groupId>com.iteima.shop</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../shop-parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>shop-pay-service</artifactId>

    <dependencies>

        <!--mybatis-springboot-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>

        <!-- MySQL连接驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!--common-->
        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--接口-->
        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-pojo</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

    </dependencies>

</project>
<!-- D:\java-test\idea2019\shop\shop-pay-service\pom.xml -->

7.2、创建 接口 类 TradeMqProducerTempMapper.java
package com.itheima.shop.mapper;

import com.itheima.shop.pojo.TradeMqProducerTemp;
import com.itheima.shop.pojo.TradeMqProducerTempExample;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface TradeMqProducerTempMapper {
    int countByExample(TradeMqProducerTempExample example);

    int deleteByExample(TradeMqProducerTempExample example);

    int deleteByPrimaryKey(String id);

    int insert(TradeMqProducerTemp record);

    int insertSelective(TradeMqProducerTemp record);

    List<TradeMqProducerTemp> selectByExample(TradeMqProducerTempExample example);

    TradeMqProducerTemp selectByPrimaryKey(String id);

    int updateByExampleSelective(@Param("record") TradeMqProducerTemp record, @Param("example") TradeMqProducerTempExample example);

    int updateByExample(@Param("record") TradeMqProducerTemp record, @Param("example") TradeMqProducerTempExample example);

    int updateByPrimaryKeySelective(TradeMqProducerTemp record);

    int updateByPrimaryKey(TradeMqProducerTemp record);
}
7.3、创建 接口 类 TradePayMapper.java
package com.itheima.shop.mapper;

import com.itheima.shop.pojo.TradePay;
import com.itheima.shop.pojo.TradePayExample;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;
@Mapper
public interface TradePayMapper {
    int countByExample(TradePayExample example);

    int deleteByExample(TradePayExample example);

    int deleteByPrimaryKey(Long payId);

    int insert(TradePay record);

    int insertSelective(TradePay record);

    List<TradePay> selectByExample(TradePayExample example);

    TradePay selectByPrimaryKey(Long payId);

    int updateByExampleSelective(@Param("record") TradePay record, @Param("example") TradePayExample example);

    int updateByExample(@Param("record") TradePay record, @Param("example") TradePayExample example);

    int updateByPrimaryKeySelective(TradePay record);

    int updateByPrimaryKey(TradePay record);
}
7.4、创建 类 PayServiceImpl.java
package com.itheima.shop.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.itheima.api.IPayService;
import com.itheima.constant.ShopCode;
import com.itheima.entity.Result;
import com.itheima.exception.CastException;
import com.itheima.shop.mapper.TradeMqProducerTempMapper;
import com.itheima.shop.mapper.TradePayMapper;
import com.itheima.shop.pojo.TradeMqProducerTemp;
import com.itheima.shop.pojo.TradePay;
import com.itheima.shop.pojo.TradePayExample;
import com.itheima.utils.IDWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Date;


@Slf4j
@Component
@Service(interfaceClass = IPayService.class)
public class PayServiceImpl implements IPayService{

    @Autowired
    private TradePayMapper tradePayMapper;

    @Autowired
    private TradeMqProducerTempMapper mqProducerTempMapper;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Autowired
    private IDWorker idWorker;

    @Value("${rocketmq.producer.group}")
    private String groupName;

    @Value("${mq.topic}")
    private String topic;

    @Value("${mq.pay.tag}")
    private String tag;

    @Override
    public Result createPayment(TradePay tradePay) {

        if(tradePay==null || tradePay.getOrderId()==null){
            CastException.cast(ShopCode.SHOP_REQUEST_PARAMETER_VALID);
        }

        //1.判断订单支付状态
        TradePayExample example = new TradePayExample();
        TradePayExample.Criteria criteria = example.createCriteria();
        criteria.andOrderIdEqualTo(tradePay.getOrderId());
        criteria.andIsPaidEqualTo(ShopCode.SHOP_PAYMENT_IS_PAID.getCode());
        int r = tradePayMapper.countByExample(example);
        if(r>0){
            CastException.cast(ShopCode.SHOP_PAYMENT_IS_PAID);
        }
        //2.设置订单的状态为未支付
        tradePay.setIsPaid(ShopCode.SHOP_ORDER_PAY_STATUS_NO_PAY.getCode());
        //3.保存支付订单
        tradePay.setPayId(idWorker.nextId());
        tradePayMapper.insert(tradePay);

        return new Result(ShopCode.SHOP_SUCCESS.getSuccess(),ShopCode.SHOP_SUCCESS.getMessage());
    }

    @Override
    public Result callbackPayment(TradePay tradePay) throws InterruptedException, RemotingException, MQClientException, MQBrokerException {
        log.info("支付回调");
        //1. 判断用户支付状态
        if(tradePay.getIsPaid().intValue()==ShopCode.SHOP_ORDER_PAY_STATUS_IS_PAY.getCode().intValue()){
            //2. 更新支付订单状态为已支付
            Long payId = tradePay.getPayId();
            TradePay pay = tradePayMapper.selectByPrimaryKey(payId);
            //判断支付订单是否存在
            if(pay==null){
                CastException.cast(ShopCode.SHOP_PAYMENT_NOT_FOUND);
            }
            pay.setIsPaid(ShopCode.SHOP_ORDER_PAY_STATUS_IS_PAY.getCode());
            int r = tradePayMapper.updateByPrimaryKeySelective(pay);
            log.info("支付订单状态改为已支付");
            if(r==1){
                //3. 创建支付成功的消息
                TradeMqProducerTemp tradeMqProducerTemp = new TradeMqProducerTemp();
                tradeMqProducerTemp.setId(String.valueOf(idWorker.nextId()));
                tradeMqProducerTemp.setGroupName(groupName);
                tradeMqProducerTemp.setMsgTopic(topic);
                tradeMqProducerTemp.setMsgTag(tag);
                tradeMqProducerTemp.setMsgKey(String.valueOf(tradePay.getPayId()));
                tradeMqProducerTemp.setMsgBody(JSON.toJSONString(tradePay));
                tradeMqProducerTemp.setCreateTime(new Date());
                //4. 将消息持久化数据库
                mqProducerTempMapper.insert(tradeMqProducerTemp);
                log.info("将支付成功消息持久化到数据库");

                //在线程池中进行处理
                threadPoolTaskExecutor.submit(new Runnable() {
                    @Override
                    public void run() {
                        //5. 发送消息到MQ
                        SendResult result = null;
                        try {
                            result = sendMessage(topic, tag, String.valueOf(tradePay.getPayId()), JSON.toJSONString(tradePay));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        if(result.getSendStatus().equals(SendStatus.SEND_OK)){
                            log.info("消息发送成功");
                            //6. 等待发送结果,如果MQ接受到消息,删除发送成功的消息
                            mqProducerTempMapper.deleteByPrimaryKey(tradeMqProducerTemp.getId());
                            log.info("持久化到数据库的消息删除");
                        }
                    }
                });

            }
            return new Result(ShopCode.SHOP_SUCCESS.getSuccess(),ShopCode.SHOP_SUCCESS.getMessage());
        }else{
            CastException.cast(ShopCode.SHOP_PAYMENT_PAY_ERROR);
            return new Result(ShopCode.SHOP_FAIL.getSuccess(),ShopCode.SHOP_FAIL.getMessage());
        }


    }

    /**
     * 发送支付成功消息
     * @param topic
     * @param tag
     * @param key
     * @param body
     */
    private SendResult sendMessage(String topic, String tag, String key, String body) throws InterruptedException, RemotingException, MQClientException, MQBrokerException {
        if(StringUtils.isEmpty(topic)){
            CastException.cast(ShopCode.SHOP_MQ_TOPIC_IS_EMPTY);
        }
        if(StringUtils.isEmpty(body)){
            CastException.cast(ShopCode.SHOP_MQ_MESSAGE_BODY_IS_EMPTY);
        }
        Message message = new Message(topic,tag,key,body.getBytes());
        SendResult sendResult = rocketMQTemplate.getProducer().send(message);
        return sendResult;
    }
}

7.5、创建 类 PayServiceApplication.java
package com.itheima.shop;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import com.itheima.utils.IDWorker;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

@SpringBootApplication
@EnableDubboConfiguration
public class PayServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(PayServiceApplication.class,args);
    }

    @Bean
    public IDWorker getBean(){
        return new IDWorker(1,2);
    }

    @Bean
    public ThreadPoolTaskExecutor getThreadPool() {

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        executor.setCorePoolSize(4);

        executor.setMaxPoolSize(8);

        executor.setQueueCapacity(100);

        executor.setKeepAliveSeconds(60);

        executor.setThreadNamePrefix("Pool-A");

        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        executor.initialize();

        return executor;

    }

}

7.6、创建 文件 TradeMqProducerTempMapper.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.itheima.shop.mapper.TradeMqProducerTempMapper" >
  <resultMap id="BaseResultMap" type="com.itheima.shop.pojo.TradeMqProducerTemp" >
    <id column="id" property="id" jdbcType="VARCHAR" />
    <result column="group_name" property="groupName" jdbcType="VARCHAR" />
    <result column="msg_topic" property="msgTopic" jdbcType="VARCHAR" />
    <result column="msg_tag" property="msgTag" jdbcType="VARCHAR" />
    <result column="msg_key" property="msgKey" jdbcType="VARCHAR" />
    <result column="msg_body" property="msgBody" jdbcType="VARCHAR" />
    <result column="msg_status" property="msgStatus" jdbcType="INTEGER" />
    <result column="create_time" property="createTime" jdbcType="TIMESTAMP" />
  </resultMap>
  <sql id="Example_Where_Clause" >
    <where >
      <foreach collection="oredCriteria" item="criteria" separator="or" >
        <if test="criteria.valid" >
          <trim prefix="(" suffix=")" prefixOverrides="and" >
            <foreach collection="criteria.criteria" item="criterion" >
              <choose >
                <when test="criterion.noValue" >
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue" >
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue" >
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue" >
                  and ${criterion.condition}
                  <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Update_By_Example_Where_Clause" >
    <where >
      <foreach collection="example.oredCriteria" item="criteria" separator="or" >
        <if test="criteria.valid" >
          <trim prefix="(" suffix=")" prefixOverrides="and" >
            <foreach collection="criteria.criteria" item="criterion" >
              <choose >
                <when test="criterion.noValue" >
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue" >
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue" >
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue" >
                  and ${criterion.condition}
                  <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Base_Column_List" >
    id, group_name, msg_topic, msg_tag, msg_key, msg_body, msg_status, create_time
  </sql>
  <select id="selectByExample" resultMap="BaseResultMap" parameterType="com.itheima.shop.pojo.TradeMqProducerTempExample" >
    select
    <if test="distinct" >
      distinct
    </if>
    <include refid="Base_Column_List" />
    from trade_mq_producer_temp
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
    <if test="orderByClause != null" >
      order by ${orderByClause}
    </if>
  </select>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" >
    select 
    <include refid="Base_Column_List" />
    from trade_mq_producer_temp
    where id = #{id,jdbcType=VARCHAR}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.String" >
    delete from trade_mq_producer_temp
    where id = #{id,jdbcType=VARCHAR}
  </delete>
  <delete id="deleteByExample" parameterType="com.itheima.shop.pojo.TradeMqProducerTempExample" >
    delete from trade_mq_producer_temp
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
  </delete>
  <insert id="insert" parameterType="com.itheima.shop.pojo.TradeMqProducerTemp" >
    insert into trade_mq_producer_temp (id, group_name, msg_topic, 
      msg_tag, msg_key, msg_body, 
      msg_status, create_time)
    values (#{id,jdbcType=VARCHAR}, #{groupName,jdbcType=VARCHAR}, #{msgTopic,jdbcType=VARCHAR}, 
      #{msgTag,jdbcType=VARCHAR}, #{msgKey,jdbcType=VARCHAR}, #{msgBody,jdbcType=VARCHAR}, 
      #{msgStatus,jdbcType=INTEGER}, #{createTime,jdbcType=TIMESTAMP})
  </insert>
  <insert id="insertSelective" parameterType="com.itheima.shop.pojo.TradeMqProducerTemp" >
    insert into trade_mq_producer_temp
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        id,
      </if>
      <if test="groupName != null" >
        group_name,
      </if>
      <if test="msgTopic != null" >
        msg_topic,
      </if>
      <if test="msgTag != null" >
        msg_tag,
      </if>
      <if test="msgKey != null" >
        msg_key,
      </if>
      <if test="msgBody != null" >
        msg_body,
      </if>
      <if test="msgStatus != null" >
        msg_status,
      </if>
      <if test="createTime != null" >
        create_time,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        #{id,jdbcType=VARCHAR},
      </if>
      <if test="groupName != null" >
        #{groupName,jdbcType=VARCHAR},
      </if>
      <if test="msgTopic != null" >
        #{msgTopic,jdbcType=VARCHAR},
      </if>
      <if test="msgTag != null" >
        #{msgTag,jdbcType=VARCHAR},
      </if>
      <if test="msgKey != null" >
        #{msgKey,jdbcType=VARCHAR},
      </if>
      <if test="msgBody != null" >
        #{msgBody,jdbcType=VARCHAR},
      </if>
      <if test="msgStatus != null" >
        #{msgStatus,jdbcType=INTEGER},
      </if>
      <if test="createTime != null" >
        #{createTime,jdbcType=TIMESTAMP},
      </if>
    </trim>
  </insert>
  <select id="countByExample" parameterType="com.itheima.shop.pojo.TradeMqProducerTempExample" resultType="java.lang.Integer" >
    select count(*) from trade_mq_producer_temp
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
  </select>
  <update id="updateByExampleSelective" parameterType="map" >
    update trade_mq_producer_temp
    <set >
      <if test="record.id != null" >
        id = #{record.id,jdbcType=VARCHAR},
      </if>
      <if test="record.groupName != null" >
        group_name = #{record.groupName,jdbcType=VARCHAR},
      </if>
      <if test="record.msgTopic != null" >
        msg_topic = #{record.msgTopic,jdbcType=VARCHAR},
      </if>
      <if test="record.msgTag != null" >
        msg_tag = #{record.msgTag,jdbcType=VARCHAR},
      </if>
      <if test="record.msgKey != null" >
        msg_key = #{record.msgKey,jdbcType=VARCHAR},
      </if>
      <if test="record.msgBody != null" >
        msg_body = #{record.msgBody,jdbcType=VARCHAR},
      </if>
      <if test="record.msgStatus != null" >
        msg_status = #{record.msgStatus,jdbcType=INTEGER},
      </if>
      <if test="record.createTime != null" >
        create_time = #{record.createTime,jdbcType=TIMESTAMP},
      </if>
    </set>
    <if test="_parameter != null" >
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByExample" parameterType="map" >
    update trade_mq_producer_temp
    set id = #{record.id,jdbcType=VARCHAR},
      group_name = #{record.groupName,jdbcType=VARCHAR},
      msg_topic = #{record.msgTopic,jdbcType=VARCHAR},
      msg_tag = #{record.msgTag,jdbcType=VARCHAR},
      msg_key = #{record.msgKey,jdbcType=VARCHAR},
      msg_body = #{record.msgBody,jdbcType=VARCHAR},
      msg_status = #{record.msgStatus,jdbcType=INTEGER},
      create_time = #{record.createTime,jdbcType=TIMESTAMP}
    <if test="_parameter != null" >
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByPrimaryKeySelective" parameterType="com.itheima.shop.pojo.TradeMqProducerTemp" >
    update trade_mq_producer_temp
    <set >
      <if test="groupName != null" >
        group_name = #{groupName,jdbcType=VARCHAR},
      </if>
      <if test="msgTopic != null" >
        msg_topic = #{msgTopic,jdbcType=VARCHAR},
      </if>
      <if test="msgTag != null" >
        msg_tag = #{msgTag,jdbcType=VARCHAR},
      </if>
      <if test="msgKey != null" >
        msg_key = #{msgKey,jdbcType=VARCHAR},
      </if>
      <if test="msgBody != null" >
        msg_body = #{msgBody,jdbcType=VARCHAR},
      </if>
      <if test="msgStatus != null" >
        msg_status = #{msgStatus,jdbcType=INTEGER},
      </if>
      <if test="createTime != null" >
        create_time = #{createTime,jdbcType=TIMESTAMP},
      </if>
    </set>
    where id = #{id,jdbcType=VARCHAR}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.itheima.shop.pojo.TradeMqProducerTemp" >
    update trade_mq_producer_temp
    set group_name = #{groupName,jdbcType=VARCHAR},
      msg_topic = #{msgTopic,jdbcType=VARCHAR},
      msg_tag = #{msgTag,jdbcType=VARCHAR},
      msg_key = #{msgKey,jdbcType=VARCHAR},
      msg_body = #{msgBody,jdbcType=VARCHAR},
      msg_status = #{msgStatus,jdbcType=INTEGER},
      create_time = #{createTime,jdbcType=TIMESTAMP}
    where id = #{id,jdbcType=VARCHAR}
  </update>
</mapper>
7.7、创建 文件 TradePayMapper.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.itheima.shop.mapper.TradePayMapper" >
  <resultMap id="BaseResultMap" type="com.itheima.shop.pojo.TradePay" >
    <id column="pay_id" property="payId" jdbcType="BIGINT" />
    <result column="order_id" property="orderId" jdbcType="BIGINT" />
    <result column="pay_amount" property="payAmount" jdbcType="DECIMAL" />
    <result column="is_paid" property="isPaid" jdbcType="INTEGER" />
  </resultMap>
  <sql id="Example_Where_Clause" >
    <where >
      <foreach collection="oredCriteria" item="criteria" separator="or" >
        <if test="criteria.valid" >
          <trim prefix="(" suffix=")" prefixOverrides="and" >
            <foreach collection="criteria.criteria" item="criterion" >
              <choose >
                <when test="criterion.noValue" >
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue" >
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue" >
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue" >
                  and ${criterion.condition}
                  <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Update_By_Example_Where_Clause" >
    <where >
      <foreach collection="example.oredCriteria" item="criteria" separator="or" >
        <if test="criteria.valid" >
          <trim prefix="(" suffix=")" prefixOverrides="and" >
            <foreach collection="criteria.criteria" item="criterion" >
              <choose >
                <when test="criterion.noValue" >
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue" >
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue" >
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue" >
                  and ${criterion.condition}
                  <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Base_Column_List" >
    pay_id, order_id, pay_amount, is_paid
  </sql>
  <select id="selectByExample" resultMap="BaseResultMap" parameterType="com.itheima.shop.pojo.TradePayExample" >
    select
    <if test="distinct" >
      distinct
    </if>
    <include refid="Base_Column_List" />
    from trade_pay
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
    <if test="orderByClause != null" >
      order by ${orderByClause}
    </if>
  </select>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select 
    <include refid="Base_Column_List" />
    from trade_pay
    where pay_id = #{payId,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
    delete from trade_pay
    where pay_id = #{payId,jdbcType=BIGINT}
  </delete>
  <delete id="deleteByExample" parameterType="com.itheima.shop.pojo.TradePayExample" >
    delete from trade_pay
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
  </delete>
  <insert id="insert" parameterType="com.itheima.shop.pojo.TradePay" >
    insert into trade_pay (pay_id, order_id, pay_amount, 
      is_paid)
    values (#{payId,jdbcType=BIGINT}, #{orderId,jdbcType=BIGINT}, #{payAmount,jdbcType=DECIMAL}, 
      #{isPaid,jdbcType=INTEGER})
  </insert>
  <insert id="insertSelective" parameterType="com.itheima.shop.pojo.TradePay" >
    insert into trade_pay
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="payId != null" >
        pay_id,
      </if>
      <if test="orderId != null" >
        order_id,
      </if>
      <if test="payAmount != null" >
        pay_amount,
      </if>
      <if test="isPaid != null" >
        is_paid,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="payId != null" >
        #{payId,jdbcType=BIGINT},
      </if>
      <if test="orderId != null" >
        #{orderId,jdbcType=BIGINT},
      </if>
      <if test="payAmount != null" >
        #{payAmount,jdbcType=DECIMAL},
      </if>
      <if test="isPaid != null" >
        #{isPaid,jdbcType=INTEGER},
      </if>
    </trim>
  </insert>
  <select id="countByExample" parameterType="com.itheima.shop.pojo.TradePayExample" resultType="java.lang.Integer" >
    select count(*) from trade_pay
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
  </select>
  <update id="updateByExampleSelective" parameterType="map" >
    update trade_pay
    <set >
      <if test="record.payId != null" >
        pay_id = #{record.payId,jdbcType=BIGINT},
      </if>
      <if test="record.orderId != null" >
        order_id = #{record.orderId,jdbcType=BIGINT},
      </if>
      <if test="record.payAmount != null" >
        pay_amount = #{record.payAmount,jdbcType=DECIMAL},
      </if>
      <if test="record.isPaid != null" >
        is_paid = #{record.isPaid,jdbcType=INTEGER},
      </if>
    </set>
    <if test="_parameter != null" >
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByExample" parameterType="map" >
    update trade_pay
    set pay_id = #{record.payId,jdbcType=BIGINT},
      order_id = #{record.orderId,jdbcType=BIGINT},
      pay_amount = #{record.payAmount,jdbcType=DECIMAL},
      is_paid = #{record.isPaid,jdbcType=INTEGER}
    <if test="_parameter != null" >
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByPrimaryKeySelective" parameterType="com.itheima.shop.pojo.TradePay" >
    update trade_pay
    <set >
      <if test="orderId != null" >
        order_id = #{orderId,jdbcType=BIGINT},
      </if>
      <if test="payAmount != null" >
        pay_amount = #{payAmount,jdbcType=DECIMAL},
      </if>
      <if test="isPaid != null" >
        is_paid = #{isPaid,jdbcType=INTEGER},
      </if>
    </set>
    where pay_id = #{payId,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.itheima.shop.pojo.TradePay" >
    update trade_pay
    set order_id = #{orderId,jdbcType=BIGINT},
      pay_amount = #{payAmount,jdbcType=DECIMAL},
      is_paid = #{isPaid,jdbcType=INTEGER}
    where pay_id = #{payId,jdbcType=BIGINT}
  </update>
</mapper>
7.8、创建 文件 application.properties
# dubbo
spring.application.name=dubbo-pay-provider
spring.dubbo.application.id=dubbo-pay-provider
spring.dubbo.application.name=dubbo-pay-provider
spring.dubbo.registry.address=zookeeper://192.168.25.140:2181;zookeeper://192.168.25.140:2182;zookeeper://192.168.25.140:2183
spring.dubbo.server=true
spring.dubbo.protocol.name=dubbo
spring.dubbo.protocol.port=20885


# DB
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/trade?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root

# Mybatis
#pojo别名扫描包
mybatis.type-aliases-package=com.itheima.shop.pojo
#加载Mybatis映射文件
mybatis.mapper-locations=classpath:com/itheima/shop/mapper/*Mapper.xml

# RocketMQ
rocketmq.name-server=192.168.25.135:9876;192.168.25.138:9876
rocketmq.producer.group=payProducerGroup

mq.topic=payTopic
mq.pay.tag=paid

7.9 创建 测试类 PayServiceTest.java
package com.itheima.test;

import com.itheima.api.IPayService;
import com.itheima.constant.ShopCode;
import com.itheima.shop.PayServiceApplication;
import com.itheima.shop.pojo.TradePay;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.IOException;
import java.math.BigDecimal;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = PayServiceApplication.class)
public class PayServiceTest {

    @Autowired
    private IPayService payService;

    @Test
    public void createPayment(){
        long orderId = 351526299216515072L;
        TradePay tradePay = new TradePay();
        tradePay.setOrderId(orderId);
        tradePay.setPayAmount(new BigDecimal(880));
        payService.createPayment(tradePay);
    }

    @Test
    public void callbackPayment() throws InterruptedException, RemotingException, MQClientException, MQBrokerException, IOException {

        long payId = 352516176372441088L;
        long orderId = 351526299216515072L;

        TradePay tradePay = new TradePay();
        tradePay.setPayId(payId);
        tradePay.setOrderId(orderId);
        tradePay.setIsPaid(ShopCode.SHOP_ORDER_PAY_STATUS_IS_PAY.getCode());
        payService.callbackPayment(tradePay);

        System.in.read();

    }

}

8、创建 shop-user-service 用户服务 工程。

8.1、在 pom.xml 中,导入依赖坐标。

<?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>shop-parent</artifactId>
        <groupId>com.iteima.shop</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../shop-parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>shop-user-service</artifactId>

    <dependencies>

        <!--mybatis-springboot-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>

        </dependency>

        <!-- MySQL连接驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-pojo</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>
<!-- D:\java-test\idea2019\shop\shop-user-service\pom.xml -->

8.2、创建 接口 类 TradeUserMapper.java
package com.itheima.shop.mapper;

import com.itheima.shop.pojo.TradeUser;
import com.itheima.shop.pojo.TradeUserExample;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface TradeUserMapper {
    int countByExample(TradeUserExample example);

    int deleteByExample(TradeUserExample example);

    int deleteByPrimaryKey(Long userId);

    int insert(TradeUser record);

    int insertSelective(TradeUser record);

    List<TradeUser> selectByExample(TradeUserExample example);

    TradeUser selectByPrimaryKey(Long userId);

    int updateByExampleSelective(@Param("record") TradeUser record, @Param("example") TradeUserExample example);

    int updateByExample(@Param("record") TradeUser record, @Param("example") TradeUserExample example);

    int updateByPrimaryKeySelective(TradeUser record);

    int updateByPrimaryKey(TradeUser record);
}
8.3、创建 接口 类 TradeUserMoneyLogMapper.java
package com.itheima.shop.mapper;

import com.itheima.shop.pojo.TradeUserMoneyLog;
import com.itheima.shop.pojo.TradeUserMoneyLogExample;
import com.itheima.shop.pojo.TradeUserMoneyLogKey;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface TradeUserMoneyLogMapper {
    int countByExample(TradeUserMoneyLogExample example);

    int deleteByExample(TradeUserMoneyLogExample example);

    int deleteByPrimaryKey(TradeUserMoneyLogKey key);

    int insert(TradeUserMoneyLog record);

    int insertSelective(TradeUserMoneyLog record);

    List<TradeUserMoneyLog> selectByExample(TradeUserMoneyLogExample example);

    TradeUserMoneyLog selectByPrimaryKey(TradeUserMoneyLogKey key);

    int updateByExampleSelective(@Param("record") TradeUserMoneyLog record, @Param("example") TradeUserMoneyLogExample example);

    int updateByExample(@Param("record") TradeUserMoneyLog record, @Param("example") TradeUserMoneyLogExample example);

    int updateByPrimaryKeySelective(TradeUserMoneyLog record);

    int updateByPrimaryKey(TradeUserMoneyLog record);
}
8.4、创建 类 CancelMQListener.java
package com.itheima.shop.mq;

import com.alibaba.fastjson.JSON;
import com.itheima.api.IUserService;
import com.itheima.constant.ShopCode;
import com.itheima.entity.MQEntity;
import com.itheima.shop.pojo.TradeUserMoneyLog;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;


@Slf4j
@Component
@RocketMQMessageListener(topic = "${mq.order.topic}",consumerGroup = "${mq.order.consumer.group.name}",messageModel = MessageModel.BROADCASTING )
public class CancelMQListener implements RocketMQListener<MessageExt>{


    @Autowired
    private IUserService userService;

    @Override
    public void onMessage(MessageExt messageExt) {

        try {
            //1.解析消息
            String body = new String(messageExt.getBody(), "UTF-8");
            MQEntity mqEntity = JSON.parseObject(body, MQEntity.class);
            log.info("接收到消息");
            if(mqEntity.getUserMoney()!=null && mqEntity.getUserMoney().compareTo(BigDecimal.ZERO)>0){
                //2.调用业务层,进行余额修改
                TradeUserMoneyLog userMoneyLog = new TradeUserMoneyLog();
                userMoneyLog.setUseMoney(mqEntity.getUserMoney());
                userMoneyLog.setMoneyLogType(ShopCode.SHOP_USER_MONEY_REFUND.getCode());
                userMoneyLog.setUserId(mqEntity.getUserId());
                userMoneyLog.setOrderId(mqEntity.getOrderId());
                userService.updateMoneyPaid(userMoneyLog);
                log.info("余额回退成功");
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            log.error("余额回退失败");
        }

    }
}

8.5、创建 类 UserServiceImpl.java
package com.itheima.shop.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.itheima.api.IUserService;
import com.itheima.constant.ShopCode;
import com.itheima.entity.Result;
import com.itheima.exception.CastException;
import com.itheima.shop.mapper.TradeUserMapper;
import com.itheima.shop.mapper.TradeUserMoneyLogMapper;
import com.itheima.shop.pojo.TradeUser;
import com.itheima.shop.pojo.TradeUserMoneyLog;
import com.itheima.shop.pojo.TradeUserMoneyLogExample;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

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

@Component
@Service(interfaceClass = IUserService.class)
public class UserServiceImpl implements IUserService{

    @Autowired
    private TradeUserMapper userMapper;

    @Autowired
    private TradeUserMoneyLogMapper userMoneyLogMapper;

    @Override
    public TradeUser findOne(Long userId) {
        if(userId==null){
            CastException.cast(ShopCode.SHOP_REQUEST_PARAMETER_VALID);
        }
        return userMapper.selectByPrimaryKey(userId);
    }

    @Override
    public Result updateMoneyPaid(TradeUserMoneyLog userMoneyLog) {
        //1.校验参数是否合法
        if(userMoneyLog==null ||
                userMoneyLog.getUserId()==null ||
                userMoneyLog.getOrderId()==null ||
                userMoneyLog.getUseMoney()==null||
                userMoneyLog.getUseMoney().compareTo(BigDecimal.ZERO)<=0){
            CastException.cast(ShopCode.SHOP_REQUEST_PARAMETER_VALID);
        }

        //2.查询订单余额使用日志
        TradeUserMoneyLogExample userMoneyLogExample = new TradeUserMoneyLogExample();
        TradeUserMoneyLogExample.Criteria criteria = userMoneyLogExample.createCriteria();
        criteria.andOrderIdEqualTo(userMoneyLog.getOrderId());
        criteria.andUserIdEqualTo(userMoneyLog.getUserId());
        int r = userMoneyLogMapper.countByExample(userMoneyLogExample);

        TradeUser tradeUser = userMapper.selectByPrimaryKey(userMoneyLog.getUserId());

        //3.扣减余额...
        if(userMoneyLog.getMoneyLogType().intValue()==ShopCode.SHOP_USER_MONEY_PAID.getCode().intValue()){
            if(r>0){
                //已经付款
                CastException.cast(ShopCode.SHOP_ORDER_PAY_STATUS_IS_PAY);
            }
            //减余额
            tradeUser.setUserMoney(new BigDecimal(tradeUser.getUserMoney()).subtract(userMoneyLog.getUseMoney()).longValue());
            userMapper.updateByPrimaryKey(tradeUser);
        }
        //4.回退余额...
        if(userMoneyLog.getMoneyLogType().intValue()==ShopCode.SHOP_USER_MONEY_REFUND.getCode().intValue()){
            if(r<0){
                //如果没有支付,则不能回退余额
                CastException.cast(ShopCode.SHOP_ORDER_PAY_STATUS_NO_PAY);
            }
            //防止多次退款
            TradeUserMoneyLogExample userMoneyLogExample2 = new TradeUserMoneyLogExample();
            TradeUserMoneyLogExample.Criteria criteria1 = userMoneyLogExample2.createCriteria();
            criteria1.andOrderIdEqualTo(userMoneyLog.getOrderId());
            criteria1.andUserIdEqualTo(userMoneyLog.getUserId());
            criteria1.andMoneyLogTypeEqualTo(ShopCode.SHOP_USER_MONEY_REFUND.getCode());
            int r2 = userMoneyLogMapper.countByExample(userMoneyLogExample2);
            if(r2>0){
                CastException.cast(ShopCode.SHOP_USER_MONEY_REFUND_ALREADY);
            }
            //退款
            tradeUser.setUserMoney(new BigDecimal(tradeUser.getUserMoney()).add(userMoneyLog.getUseMoney()).longValue());
            userMapper.updateByPrimaryKey(tradeUser);
        }
        //5.记录订单余额使用日志
        userMoneyLog.setCreateTime(new Date());
        userMoneyLogMapper.insert(userMoneyLog);
        return new Result(ShopCode.SHOP_SUCCESS.getSuccess(),ShopCode.SHOP_SUCCESS.getMessage());
    }
}

8.6、创建 类 UserServiceApplication.java
package com.itheima.shop;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubboConfiguration
public class UserServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class,args);
    }

}

8.7、创建 文件 TradeUserMapper.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.itheima.shop.mapper.TradeUserMapper" >
  <resultMap id="BaseResultMap" type="com.itheima.shop.pojo.TradeUser" >
    <id column="user_id" property="userId" jdbcType="BIGINT" />
    <result column="user_name" property="userName" jdbcType="VARCHAR" />
    <result column="user_password" property="userPassword" jdbcType="VARCHAR" />
    <result column="user_mobile" property="userMobile" jdbcType="VARCHAR" />
    <result column="user_score" property="userScore" jdbcType="INTEGER" />
    <result column="user_reg_time" property="userRegTime" jdbcType="TIMESTAMP" />
    <result column="user_money" property="userMoney" jdbcType="DECIMAL" />
  </resultMap>
  <sql id="Example_Where_Clause" >
    <where >
      <foreach collection="oredCriteria" item="criteria" separator="or" >
        <if test="criteria.valid" >
          <trim prefix="(" suffix=")" prefixOverrides="and" >
            <foreach collection="criteria.criteria" item="criterion" >
              <choose >
                <when test="criterion.noValue" >
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue" >
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue" >
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue" >
                  and ${criterion.condition}
                  <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Update_By_Example_Where_Clause" >
    <where >
      <foreach collection="example.oredCriteria" item="criteria" separator="or" >
        <if test="criteria.valid" >
          <trim prefix="(" suffix=")" prefixOverrides="and" >
            <foreach collection="criteria.criteria" item="criterion" >
              <choose >
                <when test="criterion.noValue" >
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue" >
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue" >
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue" >
                  and ${criterion.condition}
                  <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Base_Column_List" >
    user_id, user_name, user_password, user_mobile, user_score, user_reg_time, user_money
  </sql>
  <select id="selectByExample" resultMap="BaseResultMap" parameterType="com.itheima.shop.pojo.TradeUserExample" >
    select
    <if test="distinct" >
      distinct
    </if>
    <include refid="Base_Column_List" />
    from trade_user
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
    <if test="orderByClause != null" >
      order by ${orderByClause}
    </if>
  </select>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select 
    <include refid="Base_Column_List" />
    from trade_user
    where user_id = #{userId,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
    delete from trade_user
    where user_id = #{userId,jdbcType=BIGINT}
  </delete>
  <delete id="deleteByExample" parameterType="com.itheima.shop.pojo.TradeUserExample" >
    delete from trade_user
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
  </delete>
  <insert id="insert" parameterType="com.itheima.shop.pojo.TradeUser" >
    insert into trade_user (user_id, user_name, user_password, 
      user_mobile, user_score, user_reg_time, 
      user_money)
    values (#{userId,jdbcType=BIGINT}, #{userName,jdbcType=VARCHAR}, #{userPassword,jdbcType=VARCHAR}, 
      #{userMobile,jdbcType=VARCHAR}, #{userScore,jdbcType=INTEGER}, #{userRegTime,jdbcType=TIMESTAMP}, 
      #{userMoney,jdbcType=DECIMAL})
  </insert>
  <insert id="insertSelective" parameterType="com.itheima.shop.pojo.TradeUser" >
    insert into trade_user
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="userId != null" >
        user_id,
      </if>
      <if test="userName != null" >
        user_name,
      </if>
      <if test="userPassword != null" >
        user_password,
      </if>
      <if test="userMobile != null" >
        user_mobile,
      </if>
      <if test="userScore != null" >
        user_score,
      </if>
      <if test="userRegTime != null" >
        user_reg_time,
      </if>
      <if test="userMoney != null" >
        user_money,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="userId != null" >
        #{userId,jdbcType=BIGINT},
      </if>
      <if test="userName != null" >
        #{userName,jdbcType=VARCHAR},
      </if>
      <if test="userPassword != null" >
        #{userPassword,jdbcType=VARCHAR},
      </if>
      <if test="userMobile != null" >
        #{userMobile,jdbcType=VARCHAR},
      </if>
      <if test="userScore != null" >
        #{userScore,jdbcType=INTEGER},
      </if>
      <if test="userRegTime != null" >
        #{userRegTime,jdbcType=TIMESTAMP},
      </if>
      <if test="userMoney != null" >
        #{userMoney,jdbcType=DECIMAL},
      </if>
    </trim>
  </insert>
  <select id="countByExample" parameterType="com.itheima.shop.pojo.TradeUserExample" resultType="java.lang.Integer" >
    select count(*) from trade_user
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
  </select>
  <update id="updateByExampleSelective" parameterType="map" >
    update trade_user
    <set >
      <if test="record.userId != null" >
        user_id = #{record.userId,jdbcType=BIGINT},
      </if>
      <if test="record.userName != null" >
        user_name = #{record.userName,jdbcType=VARCHAR},
      </if>
      <if test="record.userPassword != null" >
        user_password = #{record.userPassword,jdbcType=VARCHAR},
      </if>
      <if test="record.userMobile != null" >
        user_mobile = #{record.userMobile,jdbcType=VARCHAR},
      </if>
      <if test="record.userScore != null" >
        user_score = #{record.userScore,jdbcType=INTEGER},
      </if>
      <if test="record.userRegTime != null" >
        user_reg_time = #{record.userRegTime,jdbcType=TIMESTAMP},
      </if>
      <if test="record.userMoney != null" >
        user_money = #{record.userMoney,jdbcType=DECIMAL},
      </if>
    </set>
    <if test="_parameter != null" >
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByExample" parameterType="map" >
    update trade_user
    set user_id = #{record.userId,jdbcType=BIGINT},
      user_name = #{record.userName,jdbcType=VARCHAR},
      user_password = #{record.userPassword,jdbcType=VARCHAR},
      user_mobile = #{record.userMobile,jdbcType=VARCHAR},
      user_score = #{record.userScore,jdbcType=INTEGER},
      user_reg_time = #{record.userRegTime,jdbcType=TIMESTAMP},
      user_money = #{record.userMoney,jdbcType=DECIMAL}
    <if test="_parameter != null" >
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByPrimaryKeySelective" parameterType="com.itheima.shop.pojo.TradeUser" >
    update trade_user
    <set >
      <if test="userName != null" >
        user_name = #{userName,jdbcType=VARCHAR},
      </if>
      <if test="userPassword != null" >
        user_password = #{userPassword,jdbcType=VARCHAR},
      </if>
      <if test="userMobile != null" >
        user_mobile = #{userMobile,jdbcType=VARCHAR},
      </if>
      <if test="userScore != null" >
        user_score = #{userScore,jdbcType=INTEGER},
      </if>
      <if test="userRegTime != null" >
        user_reg_time = #{userRegTime,jdbcType=TIMESTAMP},
      </if>
      <if test="userMoney != null" >
        user_money = #{userMoney,jdbcType=DECIMAL},
      </if>
    </set>
    where user_id = #{userId,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.itheima.shop.pojo.TradeUser" >
    update trade_user
    set user_name = #{userName,jdbcType=VARCHAR},
      user_password = #{userPassword,jdbcType=VARCHAR},
      user_mobile = #{userMobile,jdbcType=VARCHAR},
      user_score = #{userScore,jdbcType=INTEGER},
      user_reg_time = #{userRegTime,jdbcType=TIMESTAMP},
      user_money = #{userMoney,jdbcType=DECIMAL}
    where user_id = #{userId,jdbcType=BIGINT}
  </update>
</mapper>
8.8、创建 文件 TradeUserMoneyLogMapper.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.itheima.shop.mapper.TradeUserMoneyLogMapper" >
  <resultMap id="BaseResultMap" type="com.itheima.shop.pojo.TradeUserMoneyLog" >
    <id column="user_id" property="userId" jdbcType="BIGINT" />
    <id column="order_id" property="orderId" jdbcType="BIGINT" />
    <id column="money_log_type" property="moneyLogType" jdbcType="INTEGER" />
    <result column="use_money" property="useMoney" jdbcType="DECIMAL" />
    <result column="create_time" property="createTime" jdbcType="TIMESTAMP" />
  </resultMap>
  <sql id="Example_Where_Clause" >
    <where >
      <foreach collection="oredCriteria" item="criteria" separator="or" >
        <if test="criteria.valid" >
          <trim prefix="(" suffix=")" prefixOverrides="and" >
            <foreach collection="criteria.criteria" item="criterion" >
              <choose >
                <when test="criterion.noValue" >
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue" >
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue" >
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue" >
                  and ${criterion.condition}
                  <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Update_By_Example_Where_Clause" >
    <where >
      <foreach collection="example.oredCriteria" item="criteria" separator="or" >
        <if test="criteria.valid" >
          <trim prefix="(" suffix=")" prefixOverrides="and" >
            <foreach collection="criteria.criteria" item="criterion" >
              <choose >
                <when test="criterion.noValue" >
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue" >
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue" >
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue" >
                  and ${criterion.condition}
                  <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Base_Column_List" >
    user_id, order_id, money_log_type, use_money, create_time
  </sql>
  <select id="selectByExample" resultMap="BaseResultMap" parameterType="com.itheima.shop.pojo.TradeUserMoneyLogExample" >
    select
    <if test="distinct" >
      distinct
    </if>
    <include refid="Base_Column_List" />
    from trade_user_money_log
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
    <if test="orderByClause != null" >
      order by ${orderByClause}
    </if>
  </select>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="com.itheima.shop.pojo.TradeUserMoneyLogKey" >
    select 
    <include refid="Base_Column_List" />
    from trade_user_money_log
    where user_id = #{userId,jdbcType=BIGINT}
      and order_id = #{orderId,jdbcType=BIGINT}
      and money_log_type = #{moneyLogType,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="com.itheima.shop.pojo.TradeUserMoneyLogKey" >
    delete from trade_user_money_log
    where user_id = #{userId,jdbcType=BIGINT}
      and order_id = #{orderId,jdbcType=BIGINT}
      and money_log_type = #{moneyLogType,jdbcType=INTEGER}
  </delete>
  <delete id="deleteByExample" parameterType="com.itheima.shop.pojo.TradeUserMoneyLogExample" >
    delete from trade_user_money_log
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
  </delete>
  <insert id="insert" parameterType="com.itheima.shop.pojo.TradeUserMoneyLog" >
    insert into trade_user_money_log (user_id, order_id, money_log_type, 
      use_money, create_time)
    values (#{userId,jdbcType=BIGINT}, #{orderId,jdbcType=BIGINT}, #{moneyLogType,jdbcType=INTEGER}, 
      #{useMoney,jdbcType=DECIMAL}, #{createTime,jdbcType=TIMESTAMP})
  </insert>
  <insert id="insertSelective" parameterType="com.itheima.shop.pojo.TradeUserMoneyLog" >
    insert into trade_user_money_log
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="userId != null" >
        user_id,
      </if>
      <if test="orderId != null" >
        order_id,
      </if>
      <if test="moneyLogType != null" >
        money_log_type,
      </if>
      <if test="useMoney != null" >
        use_money,
      </if>
      <if test="createTime != null" >
        create_time,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="userId != null" >
        #{userId,jdbcType=BIGINT},
      </if>
      <if test="orderId != null" >
        #{orderId,jdbcType=BIGINT},
      </if>
      <if test="moneyLogType != null" >
        #{moneyLogType,jdbcType=INTEGER},
      </if>
      <if test="useMoney != null" >
        #{useMoney,jdbcType=DECIMAL},
      </if>
      <if test="createTime != null" >
        #{createTime,jdbcType=TIMESTAMP},
      </if>
    </trim>
  </insert>
  <select id="countByExample" parameterType="com.itheima.shop.pojo.TradeUserMoneyLogExample" resultType="java.lang.Integer" >
    select count(*) from trade_user_money_log
    <if test="_parameter != null" >
      <include refid="Example_Where_Clause" />
    </if>
  </select>
  <update id="updateByExampleSelective" parameterType="map" >
    update trade_user_money_log
    <set >
      <if test="record.userId != null" >
        user_id = #{record.userId,jdbcType=BIGINT},
      </if>
      <if test="record.orderId != null" >
        order_id = #{record.orderId,jdbcType=BIGINT},
      </if>
      <if test="record.moneyLogType != null" >
        money_log_type = #{record.moneyLogType,jdbcType=INTEGER},
      </if>
      <if test="record.useMoney != null" >
        use_money = #{record.useMoney,jdbcType=DECIMAL},
      </if>
      <if test="record.createTime != null" >
        create_time = #{record.createTime,jdbcType=TIMESTAMP},
      </if>
    </set>
    <if test="_parameter != null" >
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByExample" parameterType="map" >
    update trade_user_money_log
    set user_id = #{record.userId,jdbcType=BIGINT},
      order_id = #{record.orderId,jdbcType=BIGINT},
      money_log_type = #{record.moneyLogType,jdbcType=INTEGER},
      use_money = #{record.useMoney,jdbcType=DECIMAL},
      create_time = #{record.createTime,jdbcType=TIMESTAMP}
    <if test="_parameter != null" >
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByPrimaryKeySelective" parameterType="com.itheima.shop.pojo.TradeUserMoneyLog" >
    update trade_user_money_log
    <set >
      <if test="useMoney != null" >
        use_money = #{useMoney,jdbcType=DECIMAL},
      </if>
      <if test="createTime != null" >
        create_time = #{createTime,jdbcType=TIMESTAMP},
      </if>
    </set>
    where user_id = #{userId,jdbcType=BIGINT}
      and order_id = #{orderId,jdbcType=BIGINT}
      and money_log_type = #{moneyLogType,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.itheima.shop.pojo.TradeUserMoneyLog" >
    update trade_user_money_log
    set use_money = #{useMoney,jdbcType=DECIMAL},
      create_time = #{createTime,jdbcType=TIMESTAMP}
    where user_id = #{userId,jdbcType=BIGINT}
      and order_id = #{orderId,jdbcType=BIGINT}
      and money_log_type = #{moneyLogType,jdbcType=INTEGER}
  </update>
</mapper>
8.9、创建 文件 application.properties
spring.application.name=dubbo-user-provider
spring.dubbo.application.id=dubbo-user-provider
spring.dubbo.application.name=dubbo-user-provider
spring.dubbo.registry.address=zookeeper://192.168.25.140:2181;zookeeper://192.168.25.140:2182;zookeeper://192.168.25.140:2183
spring.dubbo.server=true
spring.dubbo.protocol.name=dubbo
spring.dubbo.protocol.port=20883

# DB
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/trade?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root

#spring集成Mybatis环境
#pojo别名扫描包
mybatis.type-aliases-package=com.itheima.shop.pojo
#加载Mybatis映射文件
mybatis.mapper-locations=classpath:com/itheima/shop/mapper/*Mapper.xml

# RocketMQ
rocketmq.name-server=192.168.25.135:9876;192.168.25.138:9876
rocketmq.producer.group=orderProducerGroup

mq.order.consumer.group.name=order_orderTopic_cancel_group
mq.order.topic=orderTopic

9、创建 shop-order-web 订单系统 工程。

9.1、在 pom.xml 中,导入依赖坐标。

<?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>shop-parent</artifactId>
        <groupId>com.iteima.shop</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../shop-parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>shop-order-web</artifactId>

    <dependencies>

        <!--spring-webmvc-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--API-->
        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

    </dependencies>

</project>
<!-- D:\java-test\idea2019\shop\shop-order-web\pom.xml -->

9.2、创建 类 OrderWebApplication.java
package com.itheima.shop;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@EnableDubboConfiguration
@SpringBootApplication
public class OrderWebApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderWebApplication.class,args);
    }

}

9.3、创建 类 OrderControllre.java
package com.itheima.shop.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.itheima.api.IOrderService;
import com.itheima.entity.Result;
import com.itheima.shop.pojo.TradeOrder;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/order")
public class OrderControllre {

    @Reference
    private IOrderService orderService;

    @RequestMapping("/confirm")
    public Result confirmOrder(@RequestBody TradeOrder order){
        return orderService.confirmOrder(order);
    }

}

9.4、创建 类 RestTemplateConfig.java
package com.itheima.shop.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.List;

@Configuration
public class RestTemplateConfig {


    @Bean
    @ConditionalOnMissingBean({ RestOperations.class, RestTemplate.class })
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {

        RestTemplate restTemplate = new RestTemplate(factory);

        // 使用 utf-8 编码集的 conver 替换默认的 conver(默认的 string conver 的编码集为"ISO-8859-1")
        List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
        Iterator<HttpMessageConverter<?>> iterator = messageConverters.iterator();
        while (iterator.hasNext()) {
            HttpMessageConverter<?> converter = iterator.next();
            if (converter instanceof StringHttpMessageConverter) {
                iterator.remove();
            }
        }
        messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));

        return restTemplate;
    }

    @Bean
    @ConditionalOnMissingBean({ClientHttpRequestFactory.class})
    public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        // ms
        factory.setReadTimeout(15000);
        // ms
        factory.setConnectTimeout(15000);
        return factory;
    }

}

9.5、创建 配置文件 application.properties
server.host=http://localhost
server.servlet.path=/order-web
server.port=8080

# dubbo
spring.application.name=dubbo-order-consumer
spring.dubbo.application.id=dubbo-order-consumer
spring.dubbo.application.name=dubbo-order-consumer
spring.dubbo.registry.address=zookeeper://192.168.25.140:2181;zookeeper://192.168.25.140:2182;zookeeper://192.168.25.140:2183

order.port=8080
shop.order.baseURI=${server.host}:${order.port}${server.servlet.path}
shop.order.confirm=/order/confirm
9.6、创建 测试 类 OrderWebTest.java
package com.itheima.test;

import com.itheima.entity.Result;
import com.itheima.shop.OrderWebApplication;
import com.itheima.shop.pojo.TradeOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = OrderWebApplication.class)
public class OrderWebTest {

    @Autowired
    private RestTemplate restTemplate;

    @Value("${shop.order.baseURI}")
    private String baseURI;

    @Value("${shop.order.confirm}")
    private String confirmOrderPath;

    @Test
    public void confirmOrder(){
        Long coupouId = 345988230098857984L;
        Long goodsId = 345959443973935104L;
        Long userId = 345963634385633280L;

        TradeOrder order = new TradeOrder();
        order.setGoodsId(goodsId);
        order.setUserId(userId);
        order.setCouponId(coupouId);
        order.setAddress("北京");
        order.setGoodsNumber(1);
        order.setGoodsPrice(new BigDecimal(1000));
        order.setShippingFee(BigDecimal.ZERO);
        order.setOrderAmount(new BigDecimal(1000));
        order.setMoneyPaid(new BigDecimal(100));

        Result result = restTemplate.postForEntity(baseURI + confirmOrderPath, order, Result.class).getBody();

        System.out.println(result);
    }

}

10、创建 shop-pay-web 支付系统 工程。

10.1、在 pom.xml 中,导入依赖坐标。

<?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>shop-parent</artifactId>
        <groupId>com.iteima.shop</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../shop-parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>shop-pay-web</artifactId>

    <dependencies>
        <!--spring-webmvc-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--API-->
        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.iteima.shop</groupId>
            <artifactId>shop-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>
<!-- D:\java-test\idea2019\shop\shop-pay-web\pom.xml -->

10.2、创建 类 PayWebApplication.java
package com.itheima.shop;

import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@EnableDubboConfiguration
@SpringBootApplication
public class PayWebApplication {

    public static void main(String[] args) {
        SpringApplication.run(PayWebApplication.class,args);
    }

}

10.3、创建 类 PayController.java
package com.itheima.shop.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.itheima.api.IPayService;
import com.itheima.entity.Result;
import com.itheima.shop.pojo.TradePay;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/pay")
public class PayController {

    @Reference
    private IPayService payService;

    @RequestMapping("/createPayment")
    public Result createPayment(@RequestBody TradePay pay){
        return payService.createPayment(pay);
    }

    @RequestMapping("/callBackPayment")
    public Result callBackPayment(@RequestBody TradePay pay) throws Exception {
        return payService.callbackPayment(pay);
    }

}

10.4、创建 类 RestTemplateConfig.java
package com.itheima.shop.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.List;

@Configuration
public class RestTemplateConfig {


    @Bean
    @ConditionalOnMissingBean({ RestOperations.class, RestTemplate.class })
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {

        RestTemplate restTemplate = new RestTemplate(factory);

        // 使用 utf-8 编码集的 conver 替换默认的 conver(默认的 string conver 的编码集为"ISO-8859-1")
        List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
        Iterator<HttpMessageConverter<?>> iterator = messageConverters.iterator();
        while (iterator.hasNext()) {
            HttpMessageConverter<?> converter = iterator.next();
            if (converter instanceof StringHttpMessageConverter) {
                iterator.remove();
            }
        }
        messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));

        return restTemplate;
    }

    @Bean
    @ConditionalOnMissingBean({ClientHttpRequestFactory.class})
    public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        // ms
        factory.setReadTimeout(15000);
        // ms
        factory.setConnectTimeout(15000);
        return factory;
    }

}

10.5、创建 配置文件 application.properties
server.host=http://localhost
server.servlet.path=/pay-web
server.port=9090

# dubbo
spring.application.name=dubbo-pay-consumer
spring.dubbo.application.id=dubbo-pay-consumer
spring.dubbo.application.name=dubbo-pay-consumer
spring.dubbo.registry.address=zookeeper://192.168.25.140:2181;zookeeper://192.168.25.140:2182;zookeeper://192.168.25.140:2183

pay.port=9090

shop.pay.baseURI=${server.host}:${pay.port}${server.servlet.path}

shop.pay.createPayment=/pay/createPayment

shop.pay.callbackPayment=/pay/callBackPayment


10.6、创建 测试 类 PayWebTest.java
package com.itheima.test;

import com.itheima.constant.ShopCode;
import com.itheima.entity.Result;
import com.itheima.shop.PayWebApplication;
import com.itheima.shop.pojo.TradePay;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = PayWebApplication.class)
public class PayWebTest {

    @Autowired
    private RestTemplate restTemplate;

    @Value("${shop.pay.baseURI}")
    private String baseURI;

    @Value("${shop.pay.createPayment}")
    private String createPaymentPath;

    @Value("${shop.pay.callbackPayment}")
    private String callBackPaymentPath;

    @Test
    public void createPayment(){
        long orderId = 352537369385242624L;
        TradePay tradePay = new TradePay();
        tradePay.setOrderId(orderId);
        tradePay.setPayAmount(new BigDecimal(880));

        Result result = restTemplate.postForEntity(baseURI + createPaymentPath, tradePay, Result.class).getBody();
        System.out.println(result);
    }

    @Test
    public void callBackPayment(){
        long payId = 352542415984402432L;
        long orderId = 352537369385242624L;

        TradePay tradePay = new TradePay();
        tradePay.setPayId(payId);
        tradePay.setOrderId(orderId);
        tradePay.setIsPaid(ShopCode.SHOP_ORDER_PAY_STATUS_IS_PAY.getCode());
        Result result = restTemplate.postForEntity(baseURI + callBackPaymentPath, tradePay, Result.class).getBody();
        System.out.println(result);
    }


}

11、创建 shop-pojo 实体类 工程。

11.1、在 pom.xml 中,导入依赖坐标。

<?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>shop-parent</artifactId>
        <groupId>com.iteima.shop</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../shop-parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>shop-pojo</artifactId>

</project>
<!-- D:\java-test\idea2019\shop\shop-pojo\pom.xml -->

上一节关联链接请点击:
# RocketMQ 实战:模拟电商网站场景综合案例(二)

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部