├── docker ├── redis │ ├── conf │ │ └── redis.conf │ └── dockerfile ├── nacos │ ├── dockerfile │ └── conf │ │ └── application.properties ├── mysql │ ├── dockerfile │ └── db │ │ ├── timeless_product.sql │ │ ├── timeless_seckill.sql │ │ └── nacos_config.sql ├── sh │ ├── cleanjar.sh │ ├── deploy.sh │ └── copy.sh ├── timeless-order │ ├── pay │ │ └── dockerfile │ ├── product │ │ └── dockerfile │ └── seckill │ │ └── dockerfile └── docker-compose.yml ├── timeless-order-pay-api ├── src │ └── main │ │ ├── resources │ │ └── core.properties │ │ └── java │ │ └── com │ │ └── timeless │ │ ├── domain │ │ ├── vo │ │ │ ├── PayVo.java │ │ │ ├── RefundVo.java │ │ │ └── SeckillProductVo.java │ │ ├── Product.java │ │ ├── SeckillProduct.java │ │ ├── ShopCart.java │ │ ├── OrderToProduct.java │ │ └── OrderInfo.java │ │ ├── exception │ │ ├── SystemException.java │ │ └── handle │ │ │ └── GlobalExceptionHandler.java │ │ ├── utils │ │ ├── DateTimeUtils.java │ │ ├── IdGenerateUtil.java │ │ └── SnowFlakeUtil.java │ │ ├── constants │ │ └── AppHttpCodeEnum.java │ │ ├── config │ │ ├── Knife4jConfiguration.java │ │ ├── RedisConfig.java │ │ └── RabbitMQConfig.java │ │ └── result │ │ └── ResponseResult.java └── pom.xml ├── timeless-seckill-server ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── timeless │ │ │ ├── service │ │ │ ├── ILock.java │ │ │ ├── ShopCartService.java │ │ │ ├── OrderToProductService.java │ │ │ ├── SeckillProductService.java │ │ │ ├── impl │ │ │ │ ├── ShopCartServiceImpl.java │ │ │ │ ├── OrderToProductServiceImpl.java │ │ │ │ ├── ILockImpl.java │ │ │ │ ├── SeckillProductServiceImpl.java │ │ │ │ └── OrderInfoServiceImpl.java │ │ │ └── OrderInfoService.java │ │ │ ├── mapper │ │ │ ├── ShopCartMapper.java │ │ │ ├── OrderInfoMapper.java │ │ │ ├── OrderToProductMapper.java │ │ │ └── SeckillProductMapper.java │ │ │ ├── feign │ │ │ ├── impl │ │ │ │ ├── ProductFeignImpl.java │ │ │ │ └── PayFeignImpl.java │ │ │ ├── ProductFeign.java │ │ │ └── PayFeign.java │ │ │ ├── rabbitmq │ │ │ ├── RefundErrorListener.java │ │ │ ├── OrderCancelConsumer.java │ │ │ └── RefundListener.java │ │ │ ├── SeckillServer8501.java │ │ │ └── controller │ │ │ ├── SeckillController.java │ │ │ ├── OrderInfoController.java │ │ │ ├── ShopCartController.java │ │ │ └── OrderPayController.java │ │ └── resources │ │ └── application.yml └── pom.xml ├── timeless-product-server ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── timeless │ │ │ ├── mapper │ │ │ └── ProductMapper.java │ │ │ ├── service │ │ │ ├── ProductService.java │ │ │ └── impl │ │ │ │ └── ProductServiceImpl.java │ │ │ ├── ProductServer8500.java │ │ │ └── controller │ │ │ └── ProductController.java │ │ └── resources │ │ └── application.yml └── pom.xml ├── timeless-pay-server ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── timeless │ │ │ ├── PayServer8502.java │ │ │ ├── feign │ │ │ ├── impl │ │ │ │ └── OrderInfoFeignImpl.java │ │ │ └── OrderInfoFeign.java │ │ │ ├── config │ │ │ ├── AlipayProperties.java │ │ │ └── AlipayConfig.java │ │ │ └── controller │ │ │ └── PayController.java │ │ └── resources │ │ └── application.yml └── pom.xml ├── README.md ├── pom.xml └── sql ├── timeless_product.sql └── timeless_seckill.sql /docker/redis/conf/redis.conf: -------------------------------------------------------------------------------- 1 | requirepass timeless 2 | -------------------------------------------------------------------------------- /docker/nacos/dockerfile: -------------------------------------------------------------------------------- 1 | # 基础镜像 2 | FROM nacos/nacos-server:2.0.3 3 | 4 | # author 5 | MAINTAINER timeless 6 | 7 | # 复制conf文件到路径 8 | COPY ./conf/application.properties /home/nacos/conf/application.properties 9 | -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/resources/core.properties: -------------------------------------------------------------------------------- 1 | spring.redis.host=VirtualIP 2 | spring.redis.port=6379 3 | spring.redis.password=timeless 4 | spring.cloud.nacos.discovery.server-addr=VirtualIP:8848 5 | feign.hystrix.enabled=true -------------------------------------------------------------------------------- /docker/mysql/dockerfile: -------------------------------------------------------------------------------- 1 | # 基础镜像 2 | FROM mysql:8.0.26 3 | 4 | # author 5 | MAINTAINER timeless 6 | 7 | # 执行sql脚本 8 | # `/docker-entrypoint-initdb.d/`是MySQL官方镜像中的一个特殊目录,用于存放初始化数据库的脚本文件。 9 | ADD ./db/*.sql /docker-entrypoint-initdb.d/ 10 | -------------------------------------------------------------------------------- /docker/sh/cleanjar.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 删除jar文件 4 | echo "开始清理jar文件" 5 | rm -f ../timeless-order/product/jar/timeless-product-server.jar 6 | rm -f ../timeless-order/seckill/jar/timeless-seckill-server.jar 7 | rm -f ../timeless-order/pay/jar/timeless-pay-server.jar 8 | echo "清理完成" -------------------------------------------------------------------------------- /docker/redis/dockerfile: -------------------------------------------------------------------------------- 1 | # 基础镜像 2 | FROM redis:6.0.8 3 | # author 4 | MAINTAINER timeless 5 | 6 | # 挂载目录 7 | VOLUME /home/order/redis 8 | # 创建目录 9 | RUN mkdir -p /home/order/redis 10 | # 指定路径 11 | WORKDIR /home/order/redis 12 | # 复制conf文件到路径 13 | COPY ./conf/redis.conf /home/order/redis/redis.conf 14 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/service/ILock.java: -------------------------------------------------------------------------------- 1 | package com.timeless.service; 2 | 3 | /** 4 | * @author timeless 5 | * @create 2023-07-16 10:42 6 | */ 7 | public interface ILock { 8 | 9 | 10 | boolean tryLock(long timeout); 11 | 12 | void unlock(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /docker/timeless-order/pay/dockerfile: -------------------------------------------------------------------------------- 1 | # 基础镜像 2 | FROM openjdk:8-jre 3 | # author 4 | MAINTAINER timeless 5 | 6 | # 挂载目录 7 | VOLUME /home/order 8 | # 创建目录 9 | RUN mkdir -p /home/order 10 | # 指定路径 11 | WORKDIR /home/order 12 | # 复制jar文件到路径 13 | COPY ./jar/timeless-pay-server.jar /home/order/timeless-pay-server.jar 14 | # 启动用户服务 15 | ENTRYPOINT ["java","-jar","timeless-pay-server.jar"] 16 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/mapper/ShopCartMapper.java: -------------------------------------------------------------------------------- 1 | package com.timeless.mapper; 2 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 3 | import com.timeless.domain.ShopCart; 4 | 5 | /** 6 | * (ShopCart)表数据库访问层 7 | * 8 | * @author makejava 9 | * @since 2023-06-17 14:51:09 10 | */ 11 | public interface ShopCartMapper extends BaseMapper { 12 | } 13 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/service/ShopCartService.java: -------------------------------------------------------------------------------- 1 | package com.timeless.service; 2 | import com.baomidou.mybatisplus.extension.service.IService; 3 | import com.timeless.domain.ShopCart; 4 | 5 | /** 6 | * (ShopCart)表服务接口 7 | * 8 | * @author makejava 9 | * @since 2023-06-17 14:51:24 10 | */ 11 | public interface ShopCartService extends IService { 12 | } 13 | -------------------------------------------------------------------------------- /docker/timeless-order/product/dockerfile: -------------------------------------------------------------------------------- 1 | # 基础镜像 2 | FROM openjdk:8-jre 3 | # author 4 | MAINTAINER timeless 5 | 6 | # 挂载目录 7 | VOLUME /home/order 8 | # 创建目录 9 | RUN mkdir -p /home/order 10 | # 指定路径 11 | WORKDIR /home/order 12 | # 复制jar文件到路径 13 | COPY ./jar/timeless-product-server.jar /home/order/timeless-product-server.jar 14 | # 启动用户服务 15 | ENTRYPOINT ["java","-jar","timeless-product-server.jar"] 16 | -------------------------------------------------------------------------------- /docker/timeless-order/seckill/dockerfile: -------------------------------------------------------------------------------- 1 | # 基础镜像 2 | FROM openjdk:8-jre 3 | # author 4 | MAINTAINER timeless 5 | 6 | # 挂载目录 7 | VOLUME /home/order 8 | # 创建目录 9 | RUN mkdir -p /home/order 10 | # 指定路径 11 | WORKDIR /home/order 12 | # 复制jar文件到路径 13 | COPY ./jar/timeless-seckill-server.jar /home/order/timeless-seckill-server.jar 14 | # 启动用户服务 15 | ENTRYPOINT ["java","-jar","timeless-seckill-server.jar"] 16 | -------------------------------------------------------------------------------- /timeless-product-server/src/main/java/com/timeless/mapper/ProductMapper.java: -------------------------------------------------------------------------------- 1 | package com.timeless.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.timeless.domain.Product; 5 | 6 | 7 | /** 8 | * (Product)表数据库访问层 9 | * 10 | * @author makejava 11 | * @since 2023-05-28 11:45:57 12 | */ 13 | public interface ProductMapper extends BaseMapper { 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /timeless-product-server/src/main/java/com/timeless/service/ProductService.java: -------------------------------------------------------------------------------- 1 | package com.timeless.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import com.timeless.domain.Product; 5 | 6 | 7 | /** 8 | * (Product)表服务接口 9 | * 10 | * @author makejava 11 | * @since 2023-05-28 11:45:59 12 | */ 13 | public interface ProductService extends IService { 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/mapper/OrderInfoMapper.java: -------------------------------------------------------------------------------- 1 | package com.timeless.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.timeless.domain.OrderInfo; 5 | 6 | 7 | /** 8 | * (OrderInfo)表数据库访问层 9 | * 10 | * @author makejava 11 | * @since 2023-05-28 11:51:49 12 | */ 13 | public interface OrderInfoMapper extends BaseMapper { 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/mapper/OrderToProductMapper.java: -------------------------------------------------------------------------------- 1 | package com.timeless.mapper; 2 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 3 | import com.timeless.domain.OrderToProduct; 4 | 5 | /** 6 | * (OrderToProduct)表数据库访问层 7 | * 8 | * @author makejava 9 | * @since 2023-06-17 15:48:12 10 | */ 11 | public interface OrderToProductMapper extends BaseMapper { 12 | } 13 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/service/OrderToProductService.java: -------------------------------------------------------------------------------- 1 | package com.timeless.service; 2 | import com.baomidou.mybatisplus.extension.service.IService; 3 | import com.timeless.domain.OrderToProduct; 4 | 5 | /** 6 | * (OrderToProduct)表服务接口 7 | * 8 | * @author makejava 9 | * @since 2023-06-17 15:48:14 10 | */ 11 | public interface OrderToProductService extends IService { 12 | } 13 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/mapper/SeckillProductMapper.java: -------------------------------------------------------------------------------- 1 | package com.timeless.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.timeless.domain.SeckillProduct; 5 | 6 | 7 | /** 8 | * (SeckillProduct)表数据库访问层 9 | * 10 | * @author makejava 11 | * @since 2023-05-28 11:51:04 12 | */ 13 | public interface SeckillProductMapper extends BaseMapper { 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/java/com/timeless/domain/vo/PayVo.java: -------------------------------------------------------------------------------- 1 | package com.timeless.domain.vo; 2 | 3 | import lombok.*; 4 | 5 | /** 6 | * Created by lanxw 7 | * 调用支付接口需要传入的对象 8 | */ 9 | @Data 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class PayVo { 13 | private String outTradeNo;//订单编号 14 | private String totalAmount; //付款金额,必填 15 | private String subject; //订单名称,必填 16 | private String body;//商品描述,可空 17 | private String returnUrl;//同步回调地址 18 | private String notifyUrl; //异步回调地址 19 | } 20 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/service/SeckillProductService.java: -------------------------------------------------------------------------------- 1 | package com.timeless.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import com.timeless.domain.SeckillProduct; 5 | import com.timeless.domain.vo.SeckillProductVo; 6 | 7 | 8 | /** 9 | * (SeckillProduct)表服务接口 10 | * 11 | * @author makejava 12 | * @since 2023-05-28 11:51:05 13 | */ 14 | public interface SeckillProductService extends IService { 15 | 16 | SeckillProductVo getSeckkillProductVo(Long seckillId); 17 | } 18 | 19 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/feign/impl/ProductFeignImpl.java: -------------------------------------------------------------------------------- 1 | package com.timeless.feign.impl; 2 | 3 | import com.timeless.feign.ProductFeign; 4 | import com.timeless.result.ResponseResult; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * @author timeless 9 | * @date 2023/5/28 14:04 10 | * @desciption: 11 | */ 12 | @Component 13 | public class ProductFeignImpl implements ProductFeign { 14 | @Override 15 | public ResponseResult getProductByProductId(Long productId) { 16 | return null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /timeless-pay-server/src/main/java/com/timeless/PayServer8502.java: -------------------------------------------------------------------------------- 1 | package com.timeless; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.openfeign.EnableFeignClients; 6 | 7 | /** 8 | * @author timeless 9 | * @date 2023/5/28 12:45 10 | * @desciption: 11 | */ 12 | @SpringBootApplication 13 | @EnableFeignClients 14 | public class PayServer8502 { 15 | public static void main(String[] args) { 16 | SpringApplication.run(PayServer8502.class, args); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /timeless-product-server/src/main/java/com/timeless/ProductServer8500.java: -------------------------------------------------------------------------------- 1 | package com.timeless; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | /** 8 | * @author timeless 9 | * @date 2023/5/28 12:45 10 | * @desciption: 11 | */ 12 | @SpringBootApplication 13 | @MapperScan("com.timeless.mapper") 14 | public class ProductServer8500 { 15 | public static void main(String[] args) { 16 | SpringApplication.run(ProductServer8500.class, args); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/java/com/timeless/domain/vo/RefundVo.java: -------------------------------------------------------------------------------- 1 | package com.timeless.domain.vo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * @author timeless 11 | * @create 2023-05-29 0:10 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class RefundVo implements Serializable { 17 | 18 | // 交易订单号 19 | private String outTradeNo; 20 | 21 | // 退款金额 22 | private String refundAmount; 23 | 24 | // 退款原因 25 | private String refundReason; 26 | } 27 | -------------------------------------------------------------------------------- /timeless-pay-server/src/main/java/com/timeless/feign/impl/OrderInfoFeignImpl.java: -------------------------------------------------------------------------------- 1 | package com.timeless.feign.impl; 2 | 3 | import com.timeless.domain.OrderInfo; 4 | import com.timeless.feign.OrderInfoFeign; 5 | import com.timeless.result.ResponseResult; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * @author timeless 10 | * @date 2023/5/29 16:33 11 | * @desciption: 12 | */ 13 | @Component 14 | public class OrderInfoFeignImpl implements OrderInfoFeign { 15 | @Override 16 | public ResponseResult getOrderInfoByOrderNo(String orderNo) { 17 | return null; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/service/impl/ShopCartServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.timeless.service.impl; 2 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 3 | import com.timeless.domain.ShopCart; 4 | import com.timeless.mapper.ShopCartMapper; 5 | import com.timeless.service.ShopCartService; 6 | import org.springframework.stereotype.Service; 7 | /** 8 | * (ShopCart)表服务实现类 9 | * 10 | * @author makejava 11 | * @since 2023-06-17 14:51:26 12 | */ 13 | @Service("shopCartService") 14 | public class ShopCartServiceImpl extends ServiceImpl implements ShopCartService { 15 | } 16 | -------------------------------------------------------------------------------- /timeless-product-server/src/main/java/com/timeless/service/impl/ProductServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.timeless.service.impl; 2 | 3 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 4 | import com.timeless.domain.Product; 5 | import com.timeless.mapper.ProductMapper; 6 | import com.timeless.service.ProductService; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | * (Product)表服务实现类 11 | * 12 | * @author makejava 13 | * @since 2023-05-28 11:46:00 14 | */ 15 | @Service("productService") 16 | public class ProductServiceImpl extends ServiceImpl implements ProductService { 17 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/java/com/timeless/domain/vo/SeckillProductVo.java: -------------------------------------------------------------------------------- 1 | package com.timeless.domain.vo; 2 | 3 | import com.timeless.domain.SeckillProduct; 4 | import lombok.*; 5 | 6 | import java.io.Serializable; 7 | import java.math.BigDecimal; 8 | 9 | /** 10 | * Created by timeless-lanxw 11 | * 把Seckillproduct和product的信息整合在一块,然后再前台展示. 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class SeckillProductVo extends SeckillProduct implements Serializable { 17 | private String productName; 18 | private String productTitle; 19 | private String productDetail; 20 | private Double productPrice; 21 | private Integer currentCount; 22 | } 23 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/service/impl/OrderToProductServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.timeless.service.impl; 2 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 3 | import com.timeless.domain.OrderToProduct; 4 | import com.timeless.mapper.OrderToProductMapper; 5 | import com.timeless.service.OrderToProductService; 6 | import org.springframework.stereotype.Service; 7 | /** 8 | * (OrderToProduct)表服务实现类 9 | * 10 | * @author makejava 11 | * @since 2023-06-17 15:48:15 12 | */ 13 | @Service("orderToProductService") 14 | public class OrderToProductServiceImpl extends ServiceImpl implements OrderToProductService { 15 | } 16 | -------------------------------------------------------------------------------- /docker/sh/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 使用说明,用来提示输入参数 4 | usage(){ 5 | echo "Usage: sh 执行脚本.sh [base|services|stop|rm]" 6 | exit 1 7 | } 8 | 9 | # 启动基础环境(必须) 10 | base(){ 11 | docker-compose up -d order-mysql order-redis order-nacos 12 | } 13 | 14 | # 启动程序模块(必须) 15 | services(){ 16 | docker-compose up -d order-product order-seckill order-pay 17 | } 18 | 19 | # 关闭所有环境/模块 20 | stop(){ 21 | docker-compose stop 22 | } 23 | 24 | # 删除所有环境/模块 25 | rm(){ 26 | docker-compose rm 27 | } 28 | 29 | # 根据输入参数,选择执行对应方法,不输入则执行使用说明 30 | case "$1" in 31 | "base") 32 | base 33 | ;; 34 | "services") 35 | services 36 | ;; 37 | "stop") 38 | stop 39 | ;; 40 | "rm") 41 | rm 42 | ;; 43 | *) 44 | usage 45 | ;; 46 | esac 47 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/rabbitmq/RefundErrorListener.java: -------------------------------------------------------------------------------- 1 | package com.timeless.rabbitmq; 2 | 3 | import com.timeless.config.RabbitMQConfig; 4 | import com.timeless.domain.vo.RefundVo; 5 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class RefundErrorListener { 11 | 12 | 13 | @RabbitListener(queues = RabbitMQConfig.REFUND_ERROR_QUEUE) 14 | public void handleRefundError(RefundVo refundVo) { 15 | 16 | System.out.println("退款又失败了,还是通知客服操作吧,呜呜呜......"); 17 | 18 | // 发送退款失败的通知邮件 19 | 20 | 21 | } 22 | } -------------------------------------------------------------------------------- /timeless-product-server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8500 3 | spring: 4 | application: 5 | name: timeless-product-server 6 | datasource: 7 | url: jdbc:mysql://VirtualIP:3306/timeless_product?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai 8 | username: root 9 | password: root 10 | driver-class-name: com.mysql.cj.jdbc.Driver 11 | # cloud: 12 | # nacos: 13 | # discovery: 14 | # server-addr: VirtualIP:8848 15 | # rabbitmq: 16 | # host: 127.0.0.1 17 | # port: 5672 18 | # username: guest 19 | # password: guest 20 | 21 | mybatis-plus: 22 | configuration: 23 | log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # mybatis日志 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # timeless-order-pay-parent 2 | 订单支付系统的一致性解决方案。 3 | 如遇到问题,请联系博主。 4 | QQ:1051346016。 5 | WX:timeless192194。 6 | 7 | 8 | 2023-6-17 : 9 | 10 | 新增:购物车、自定义订单超时取消时间、商品批量下单(这些商品合并成为一个订单,只有一个订单号)、基于插件的延迟队列(应对队列先进先出的特性)。 11 | 12 | 本次分享主题: 1. 以上新增功能的讲解 2. 上个视频评论区问题的解决。(不同延迟时间的消息在队列中阻塞) 13 | 14 | 在观看此视频前,请观看上上个视频:RabbitMQ实现订单超时取消。上个视频讲过的东西,我在本视频不再赘述。 15 | 16 | 2023-7-17: 17 | 18 | redis分布式锁解决超卖、一人一单。 19 | 20 | 2023-7-18: 21 | 22 | 本次分享主题: docker-compose 部署 微服务项目(后端) 23 | 24 | docker中安装:nacos:2.0.3 、 mysql:8.0.26 、 redis:6.0.8 、 三个微服务模块(product 、 seckill 、 pay) 25 | 26 | 在观看本视频之前,请对docker、dockerfile和docker-compose有一定了解,知道基础命令。 27 | 28 | 另外,本项目的具体讲解可以在我往期的视频找到,这边不做赘述,只是讲一下需要修改的配置和docker-compose。 -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/java/com/timeless/exception/SystemException.java: -------------------------------------------------------------------------------- 1 | package com.timeless.exception; 2 | 3 | import com.timeless.constants.AppHttpCodeEnum; 4 | 5 | /** 6 | * @Author 三更 B站: https://space.bilibili.com/663528522 7 | */ 8 | public class SystemException extends RuntimeException{ 9 | 10 | private final int code; 11 | 12 | private final String msg; 13 | 14 | public int getCode() { 15 | return code; 16 | } 17 | 18 | public String getMsg() { 19 | return msg; 20 | } 21 | 22 | public SystemException(AppHttpCodeEnum httpCodeEnum) { 23 | super(httpCodeEnum.getMsg()); 24 | this.code = httpCodeEnum.getCode(); 25 | this.msg = httpCodeEnum.getMsg(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/java/com/timeless/domain/Product.java: -------------------------------------------------------------------------------- 1 | package com.timeless.domain; 2 | 3 | 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | /** 11 | * (Product)表实体类 12 | * 13 | * @author makejava 14 | * @since 2023-05-28 11:45:59 15 | */ 16 | @SuppressWarnings("serial") 17 | @Data 18 | @AllArgsConstructor 19 | @NoArgsConstructor 20 | @TableName("t_product") 21 | public class Product { 22 | @TableId 23 | private Long id; 24 | 25 | private String productName; 26 | 27 | private String productTitle; 28 | 29 | private String productDetail; 30 | 31 | private Double productPrice; 32 | 33 | 34 | } 35 | 36 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/SeckillServer8501.java: -------------------------------------------------------------------------------- 1 | package com.timeless; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cloud.openfeign.EnableFeignClients; 7 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 8 | 9 | /** 10 | * @author timeless 11 | * @date 2023/5/28 12:45 12 | * @desciption: 13 | */ 14 | @SpringBootApplication 15 | @EnableFeignClients 16 | @EnableAspectJAutoProxy(exposeProxy = true) 17 | @MapperScan("com.timeless.mapper") 18 | public class SeckillServer8501 { 19 | public static void main(String[] args) { 20 | SpringApplication.run(SeckillServer8501.class, args); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/java/com/timeless/domain/SeckillProduct.java: -------------------------------------------------------------------------------- 1 | package com.timeless.domain; 2 | 3 | 4 | import java.io.Serializable; 5 | 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | import com.baomidou.mybatisplus.annotation.TableId; 10 | import com.baomidou.mybatisplus.annotation.TableName; 11 | 12 | /** 13 | * (SeckillProduct)表实体类 14 | * 15 | * @author makejava 16 | * @since 2023-05-28 11:51:04 17 | */ 18 | @SuppressWarnings("serial") 19 | @Data 20 | @AllArgsConstructor 21 | @NoArgsConstructor 22 | @TableName("t_seckill_product") 23 | public class SeckillProduct { 24 | @TableId 25 | private Long id; 26 | 27 | private Long productId; 28 | 29 | private Double seckillPrice; 30 | 31 | private Integer stockCount; 32 | 33 | 34 | } 35 | 36 | -------------------------------------------------------------------------------- /docker/sh/copy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 创建目标目录 4 | mkdir -p ../mysql/db 5 | mkdir -p ../timeless-order/product/jar 6 | mkdir -p ../timeless-order/seckill/jar 7 | mkdir -p ../timeless-order/pay/jar 8 | 9 | # 复制sql文件 10 | echo "begin copy sql " 11 | cp ../../sql/timeless_seckill.sql ../mysql/db 12 | cp ../../sql/timeless_product.sql ../mysql/db 13 | echo "end copy sql " 14 | 15 | # 复制jar文件 16 | echo "begin copy jar " 17 | cp ../../timeless-product-server/target/timeless-product-server-1.0-SNAPSHOT.jar ../timeless-order/product/jar/timeless-product-server.jar 18 | cp ../../timeless-seckill-server/target/timeless-seckill-server-1.0-SNAPSHOT.jar ../timeless-order/seckill/jar/timeless-seckill-server.jar 19 | cp ../../timeless-pay-server/target/timeless-pay-server-1.0-SNAPSHOT.jar ../timeless-order/pay/jar/timeless-pay-server.jar 20 | echo "end copy jar " -------------------------------------------------------------------------------- /timeless-pay-server/src/main/java/com/timeless/config/AlipayProperties.java: -------------------------------------------------------------------------------- 1 | package com.timeless.config; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * Created by timeless-lanxw 10 | */ 11 | @Setter 12 | @Getter 13 | @Component 14 | @ConfigurationProperties(prefix = "alipay") 15 | public class AlipayProperties { 16 | //商品应用ID 17 | private String appId; 18 | // 商户私钥,您的PKCS8格式RSA2私钥 19 | private String merchantPrivateKey; 20 | // 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。 21 | private String alipayPublicKey; 22 | // 签名方式 23 | private String signType ; 24 | // 字符编码格式 25 | private String charset; 26 | // 支付宝网关 27 | private String gatewayUrl; 28 | } 29 | -------------------------------------------------------------------------------- /timeless-pay-server/src/main/java/com/timeless/config/AlipayConfig.java: -------------------------------------------------------------------------------- 1 | package com.timeless.config; 2 | 3 | import com.alipay.api.AlipayClient; 4 | import com.alipay.api.DefaultAlipayClient; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | /** 9 | * Created by timeless-lanxw 10 | */ 11 | @Configuration 12 | public class AlipayConfig { 13 | @Bean 14 | public AlipayClient alipayClient(AlipayProperties alipayProperties) { 15 | return new DefaultAlipayClient(alipayProperties.getGatewayUrl(), 16 | alipayProperties.getAppId(), 17 | alipayProperties.getMerchantPrivateKey(), 18 | "json", 19 | alipayProperties.getCharset(), 20 | alipayProperties.getAlipayPublicKey(), 21 | alipayProperties.getSignType()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/feign/ProductFeign.java: -------------------------------------------------------------------------------- 1 | package com.timeless.feign; 2 | 3 | import com.timeless.feign.impl.ProductFeignImpl; 4 | import com.timeless.result.ResponseResult; 5 | import org.springframework.cloud.openfeign.FeignClient; 6 | import org.springframework.stereotype.Component; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestParam; 10 | 11 | /** 12 | * @author timeless 13 | * @date 2023/5/28 14:03 14 | * @desciption: 15 | */ 16 | @FeignClient(name = "timeless-product-server" , fallback = ProductFeignImpl.class) 17 | @Component 18 | public interface ProductFeign { 19 | 20 | @GetMapping("/product/getProductByProductId") 21 | public ResponseResult getProductByProductId(@RequestParam("productId") Long productId); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/java/com/timeless/domain/ShopCart.java: -------------------------------------------------------------------------------- 1 | package com.timeless.domain; 2 | import java.io.Serializable; 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import com.baomidou.mybatisplus.annotation.TableId; 7 | import com.baomidou.mybatisplus.annotation.TableName; 8 | /** 9 | * (ShopCart)表实体类 10 | * 11 | * @author makejava 12 | * @since 2023-06-17 14:51:10 13 | */ 14 | @SuppressWarnings("serial") 15 | @Data 16 | @AllArgsConstructor 17 | @NoArgsConstructor 18 | @TableName("t_shop_cart") 19 | public class ShopCart { 20 | 21 | @TableId 22 | private Integer id; 23 | //购物车id 24 | private String shopCartId; 25 | //用户id 26 | private Long userId; 27 | //商品id 28 | private Long productId; 29 | //商品数量 30 | private Integer productCount; 31 | 32 | private Double productPrice; 33 | } 34 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/service/OrderInfoService.java: -------------------------------------------------------------------------------- 1 | package com.timeless.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import com.timeless.domain.OrderInfo; 5 | import com.timeless.domain.vo.SeckillProductVo; 6 | import com.timeless.result.ResponseResult; 7 | 8 | import java.util.List; 9 | 10 | 11 | /** 12 | * (OrderInfo)表服务接口 13 | * 14 | * @author makejava 15 | * @since 2023-05-28 11:51:49 16 | */ 17 | public interface OrderInfoService extends IService { 18 | 19 | OrderInfo doSeckill(Long userId, Long seckillId, String expireTime); 20 | 21 | ResponseResult pay(String orderNo); 22 | 23 | void refund(OrderInfo orderInfo); 24 | 25 | OrderInfo createOrderFromShopCart(Long userId, List productId, String expireTime); 26 | 27 | OrderInfo saveOrder(Long userId, Long seckillId, SeckillProductVo seckillProductVo); 28 | } 29 | 30 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/feign/impl/PayFeignImpl.java: -------------------------------------------------------------------------------- 1 | package com.timeless.feign.impl; 2 | 3 | import com.timeless.domain.vo.PayVo; 4 | import com.timeless.domain.vo.RefundVo; 5 | import com.timeless.feign.PayFeign; 6 | import com.timeless.result.ResponseResult; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.util.Map; 10 | 11 | /** 12 | * @author timeless 13 | * @date 2023/5/28 16:25 14 | * @desciption: 15 | */ 16 | @Component 17 | public class PayFeignImpl implements PayFeign { 18 | @Override 19 | public ResponseResult payOnline(PayVo payVo) { 20 | System.out.println("走熔断了......."); 21 | return null; 22 | } 23 | 24 | @Override 25 | public ResponseResult rsaCheckV1(Map params) { 26 | return null; 27 | } 28 | 29 | @Override 30 | public ResponseResult refund(RefundVo refundVo) { 31 | return null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /timeless-pay-server/src/main/java/com/timeless/feign/OrderInfoFeign.java: -------------------------------------------------------------------------------- 1 | package com.timeless.feign; 2 | 3 | import com.timeless.domain.OrderInfo; 4 | import com.timeless.feign.impl.OrderInfoFeignImpl; 5 | import com.timeless.result.ResponseResult; 6 | import org.springframework.cloud.openfeign.FeignClient; 7 | import org.springframework.stereotype.Component; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | 12 | /** 13 | * @author timeless 14 | * @date 2023/5/29 16:32 15 | * @desciption: 16 | */ 17 | @FeignClient(value = "timeless-seckill-server" , fallback = OrderInfoFeignImpl.class) 18 | @Component 19 | public interface OrderInfoFeign { 20 | 21 | @GetMapping("/orderInfo/getOrderInfoByOrderNo/{orderNo}") 22 | public ResponseResult getOrderInfoByOrderNo(@PathVariable("orderNo") String orderNo); 23 | 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/java/com/timeless/domain/OrderToProduct.java: -------------------------------------------------------------------------------- 1 | package com.timeless.domain; 2 | import java.io.Serializable; 3 | import java.lang.reflect.Type; 4 | 5 | import com.baomidou.mybatisplus.annotation.IdType; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | import com.baomidou.mybatisplus.annotation.TableId; 11 | import com.baomidou.mybatisplus.annotation.TableName; 12 | /** 13 | * (OrderToProduct)表实体类 14 | * 15 | * @author makejava 16 | * @since 2023-06-17 15:48:13 17 | */ 18 | @SuppressWarnings("serial") 19 | @Data 20 | @AllArgsConstructor 21 | @NoArgsConstructor 22 | @Builder 23 | @TableName("t_order_to_product") 24 | public class OrderToProduct { 25 | 26 | @TableId(type = IdType.AUTO) 27 | private Long id; 28 | //订单编号 29 | private String orderNo; 30 | //商品id 31 | private Long productId; 32 | //商品数量 33 | private Integer productCount; 34 | } 35 | -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/java/com/timeless/utils/DateTimeUtils.java: -------------------------------------------------------------------------------- 1 | package com.timeless.utils; 2 | 3 | import java.time.LocalDateTime; 4 | import java.time.format.DateTimeFormatter; 5 | 6 | /** 7 | * @Description: 生成标准时间格式,类如: 2023-5-20 12:5:20 8 | * @Date: 2023/5/29 19:46 9 | * @Author: timeless 10 | */ 11 | public class DateTimeUtils { 12 | private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; 13 | 14 | public static String getCurrentDateTime() { 15 | LocalDateTime now = LocalDateTime.now(); 16 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT); 17 | return now.format(formatter); 18 | } 19 | 20 | public static String getCurrentDateTimePlusOneMinute(Long expireTime) { 21 | LocalDateTime now = LocalDateTime.now(); 22 | LocalDateTime plusOneMinute = now.plusMinutes(expireTime); 23 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT); 24 | return plusOneMinute.format(formatter); 25 | } 26 | } -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/feign/PayFeign.java: -------------------------------------------------------------------------------- 1 | package com.timeless.feign; 2 | 3 | import com.timeless.domain.vo.PayVo; 4 | import com.timeless.domain.vo.RefundVo; 5 | import com.timeless.feign.impl.PayFeignImpl; 6 | import com.timeless.result.ResponseResult; 7 | import org.springframework.cloud.openfeign.FeignClient; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.web.bind.annotation.*; 10 | 11 | import java.util.Map; 12 | 13 | /** 14 | * @author timeless 15 | * @date 2023/5/28 16:23 16 | * @desciption: 17 | */ 18 | @FeignClient(name = "timeless-pay-server" , fallback = PayFeignImpl.class) 19 | @Component 20 | public interface PayFeign { 21 | 22 | @RequestMapping("/aliPay/payOnline") 23 | public ResponseResult payOnline(@RequestBody PayVo payVo); 24 | 25 | @RequestMapping("/aliPay/rsaCheckV1") 26 | ResponseResult rsaCheckV1(@RequestParam Map params); 27 | 28 | @RequestMapping("/aliPay/refund") 29 | ResponseResult refund(@RequestBody RefundVo refundVo); 30 | } 31 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8501 3 | spring: 4 | application: 5 | name: timeless-seckill-server 6 | datasource: 7 | url: jdbc:mysql://VirtualIP:3306/timeless_seckill?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai 8 | username: root 9 | password: root 10 | driver-class-name: com.mysql.cj.jdbc.Driver 11 | # cloud: 12 | # nacos: 13 | # discovery: 14 | # server-addr: VirtualIP:8848 15 | rabbitmq: 16 | host: VirtualIP 17 | port: 5672 18 | username: 19 | password: 20 | virtual-host: /subtlechat 21 | profiles: 22 | active: dev 23 | # rabbitmq: 24 | # host: localhost 25 | # port: 5672 26 | # username: guest 27 | # password: guest 28 | 29 | mybatis-plus: 30 | configuration: 31 | log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # mybatis日志 32 | 33 | alipay: 34 | returnUrl: http://8bcsm2.natappfree.cc/orderPay/returnUrl 35 | notifyUrl: http://8bcsm2.natappfree.cc/orderPay/notifyUrl 36 | 37 | #feign: 38 | # hystrix: 39 | # enabled: true -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/java/com/timeless/constants/AppHttpCodeEnum.java: -------------------------------------------------------------------------------- 1 | package com.timeless.constants; 2 | 3 | /** 4 | * @author 许 5 | */ 6 | 7 | public enum AppHttpCodeEnum { 8 | // 成功 9 | SUCCESS(20000, "操作成功"), 10 | SYSTEM_ERROR(20001, "服务器内部错误"), 11 | STOCK_NOT_ENOUGH(20002, "库存不足"), 12 | SECKILL_PRODUCT_NOT_EXIST(20003, "秒杀商品不存在"), 13 | CONTINUE_PAY(20004, "待付款"), 14 | DONE_PAY(20005, "已付款"), 15 | RSACHECK_FAIL(20006, "验证签名失败!"), 16 | REFUND_FAIL(20007, "退款失败!"), 17 | DONE_REFUND(20008, "已退款"), 18 | ORDER_CANCEL(20009, "已取消"), 19 | PAY_FAIL(20010, "付款失败!"), 20 | 21 | ORDER_TIMEOUT(20011, "订单超时取消!"), 22 | PRODUCT_NOT_EXIST(20012, "商品不存在"), 23 | HAS_BUY(20013, "您已抢购"); 24 | 25 | 26 | public static final Long USERID = 1000L; 27 | int code; 28 | String msg; 29 | 30 | AppHttpCodeEnum(int code, String Message) { 31 | this.code = code; 32 | this.msg = Message; 33 | } 34 | 35 | public int getCode() { 36 | return code; 37 | } 38 | 39 | public String getMsg() { 40 | return msg; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /timeless-product-server/src/main/java/com/timeless/controller/ProductController.java: -------------------------------------------------------------------------------- 1 | package com.timeless.controller; 2 | 3 | import com.timeless.domain.Product; 4 | import com.timeless.result.ResponseResult; 5 | import com.timeless.service.ProductService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestParam; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | /** 13 | * @author timeless 14 | * @date 2023/5/28 12:01 15 | * @desciption: 16 | */ 17 | @RestController 18 | @RequestMapping("/product") 19 | public class ProductController { 20 | 21 | @Autowired 22 | private ProductService productService; 23 | 24 | @GetMapping("/getProductByProductId") 25 | public ResponseResult getProductByProductId(@RequestParam("productId") Long productId){ 26 | Product product = productService.getById(productId); 27 | return ResponseResult.okResult(product); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/java/com/timeless/domain/OrderInfo.java: -------------------------------------------------------------------------------- 1 | package com.timeless.domain; 2 | 3 | import java.util.Date; 4 | 5 | import java.io.Serializable; 6 | 7 | import lombok.AllArgsConstructor; 8 | import lombok.Builder; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | import com.baomidou.mybatisplus.annotation.TableId; 12 | import com.baomidou.mybatisplus.annotation.TableName; 13 | 14 | /** 15 | * (OrderInfo)表实体类 16 | * 17 | * @author makejava 18 | * @since 2023-05-28 11:51:49 19 | */ 20 | @SuppressWarnings("serial") 21 | @Data 22 | @AllArgsConstructor 23 | @NoArgsConstructor 24 | @TableName("t_order_info") 25 | @Builder 26 | public class OrderInfo { 27 | @TableId 28 | private String orderNo; 29 | 30 | private Long userId; 31 | 32 | private Long productId; 33 | 34 | private Long seckillId; 35 | 36 | private String productName; 37 | 38 | private Double productPrice; 39 | 40 | private Double seckillPrice; 41 | 42 | private String status; 43 | 44 | private Date createDate; 45 | 46 | private Date payDate; 47 | 48 | private Date seckillDate; 49 | 50 | private Double payPrice; 51 | 52 | 53 | } 54 | 55 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/controller/SeckillController.java: -------------------------------------------------------------------------------- 1 | package com.timeless.controller; 2 | 3 | import com.timeless.domain.SeckillProduct; 4 | import com.timeless.result.ResponseResult; 5 | import com.timeless.service.SeckillProductService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestParam; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | /** 13 | * @author timeless 14 | * @date 2023/5/28 12:01 15 | * @desciption: 16 | */ 17 | @RestController 18 | @RequestMapping("/seckill") 19 | public class SeckillController { 20 | 21 | @Autowired 22 | private SeckillProductService seckillProductService; 23 | 24 | @GetMapping("/getSeckillProductBySeckillId") 25 | public ResponseResult getSeckillProductBySeckillId(@RequestParam("seckillId") Long seckillId){ 26 | SeckillProduct seckillProduct = seckillProductService.getById(seckillId); 27 | return ResponseResult.okResult(seckillProduct); 28 | } 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/service/impl/ILockImpl.java: -------------------------------------------------------------------------------- 1 | package com.timeless.service.impl; 2 | 3 | import com.timeless.service.ILock; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.data.redis.core.StringRedisTemplate; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | 10 | /** 11 | * @author timeless 12 | * @create 2023-07-16 10:43 13 | */ 14 | public class ILockImpl implements ILock { 15 | 16 | private final StringRedisTemplate stringRedisTemplate; 17 | 18 | private final String name; 19 | 20 | public ILockImpl(StringRedisTemplate stringRedisTemplate, String name) { 21 | this.stringRedisTemplate = stringRedisTemplate; 22 | this.name = name; 23 | } 24 | 25 | private static final String PREFIX = "lock:"; 26 | 27 | 28 | @Override 29 | public boolean tryLock(long timeout) { 30 | 31 | String key = PREFIX + name; 32 | String value = Thread.currentThread().getId() + ""; 33 | return Boolean.TRUE.equals(stringRedisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.SECONDS)); 34 | 35 | } 36 | 37 | @Override 38 | public void unlock() { 39 | stringRedisTemplate.delete(PREFIX + name); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/java/com/timeless/exception/handle/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.timeless.exception.handle; 2 | 3 | import com.timeless.constants.AppHttpCodeEnum; 4 | import com.timeless.exception.SystemException; 5 | import com.timeless.result.ResponseResult; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | import org.springframework.web.bind.annotation.RestControllerAdvice; 9 | 10 | import javax.servlet.http.HttpServletResponse; 11 | 12 | @RestControllerAdvice 13 | @Slf4j 14 | public class GlobalExceptionHandler { 15 | 16 | @ExceptionHandler(SystemException.class) 17 | public ResponseResult systemExceptionHandler(SystemException e, HttpServletResponse response) { 18 | //打印异常信息 19 | log.error("出现了异常! {}", e); 20 | //从异常对象中获取提示信息封装返回 21 | response.setStatus(500); 22 | return ResponseResult.errorResult(e.getCode(), e.getMsg()); 23 | } 24 | 25 | 26 | @ExceptionHandler(Exception.class) 27 | public ResponseResult exceptionHandler(Exception e, HttpServletResponse response) { 28 | //打印异常信息 29 | log.error("出现了异常! {}", e); 30 | //从异常对象中获取提示信息封装返回 31 | response.setStatus(500); 32 | return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR.getCode(), e.getMessage()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /docker/nacos/conf/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.platform=mysql 2 | db.num=1 3 | db.url.0=jdbc:mysql://VirtualIP:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC 4 | db.user=root 5 | db.password=root 6 | 7 | nacos.naming.empty-service.auto-clean=true 8 | nacos.naming.empty-service.clean.initial-delay-ms=50000 9 | nacos.naming.empty-service.clean.period-time-ms=30000 10 | 11 | management.endpoints.web.exposure.include=* 12 | 13 | management.metrics.export.elastic.enabled=false 14 | management.metrics.export.influx.enabled=false 15 | 16 | server.tomcat.accesslog.enabled=true 17 | server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i %{Request-Source}i 18 | 19 | server.tomcat.basedir=/home/order/nacos/tomcat/logs 20 | 21 | nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-ui/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/** 22 | 23 | nacos.core.auth.system.type=nacos 24 | nacos.core.auth.enabled=false 25 | nacos.core.auth.default.token.expire.seconds=18000 26 | nacos.core.auth.default.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789 27 | nacos.core.auth.caching.enabled=true 28 | nacos.core.auth.enable.userAgentAuthWhite=false 29 | nacos.core.auth.server.identity.key=serverIdentity 30 | nacos.core.auth.server.identity.value=security 31 | 32 | nacos.istio.mcp.server.enabled=false 33 | -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/java/com/timeless/config/Knife4jConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.timeless.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import springfox.documentation.builders.ApiInfoBuilder; 6 | import springfox.documentation.builders.PathSelectors; 7 | import springfox.documentation.builders.RequestHandlerSelectors; 8 | import springfox.documentation.spi.DocumentationType; 9 | import springfox.documentation.spring.web.plugins.Docket; 10 | import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc; 11 | 12 | @Configuration 13 | @EnableSwagger2WebMvc 14 | public class Knife4jConfiguration { 15 | 16 | @Bean(value = "defaultApi2") 17 | public Docket defaultApi2() { 18 | Docket docket=new Docket(DocumentationType.SWAGGER_2) 19 | .apiInfo(new ApiInfoBuilder() 20 | //.title("swagger-bootstrap-ui-demo RESTful APIs") 21 | .description("# 梨花不等故人来") 22 | .termsOfServiceUrl("https://www.timeless192194.cn/") 23 | .contact("1051346016@qq.com") 24 | .version("1.0") 25 | .build()) 26 | //分组名称 27 | .groupName("2.X版本") 28 | .select() 29 | //这里指定Controller扫描包路径 30 | .apis(RequestHandlerSelectors.basePackage("com.timeless.controller")) 31 | .paths(PathSelectors.any()) 32 | .build(); 33 | return docket; 34 | } 35 | } -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/java/com/timeless/config/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package com.timeless.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.context.annotation.PropertySource; 6 | import org.springframework.data.redis.connection.RedisConnectionFactory; 7 | import org.springframework.data.redis.core.RedisTemplate; 8 | import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; 9 | import org.springframework.data.redis.serializer.RedisSerializer; 10 | 11 | /** 12 | * @author timeless 13 | * @create 2023-07-12 0:53 14 | */ 15 | @Configuration 16 | @PropertySource("classpath:core.properties") 17 | public class RedisConfig { 18 | 19 | @Bean 20 | public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { 21 | //创建redisTemplate 22 | RedisTemplate redisTemplate = new RedisTemplate<>(); 23 | //设置连接工厂 24 | redisTemplate.setConnectionFactory(connectionFactory); 25 | //创建JSON序列化工具 26 | GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); 27 | //设置key和hashKey序列化 28 | redisTemplate.setKeySerializer(RedisSerializer.string()); 29 | redisTemplate.setHashKeySerializer(RedisSerializer.string()); 30 | //设置val和hashVal序列化 31 | redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer); 32 | redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer); 33 | //返回 34 | return redisTemplate; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /timeless-product-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | timeless-order-pay-parent 7 | com.timeless 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | timeless-product-server 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | 22 | com.timeless 23 | timeless-order-pay-api 24 | 1.0-SNAPSHOT 25 | 26 | 27 | 28 | com.baomidou 29 | mybatis-plus-boot-starter 30 | 31 | 32 | 33 | mysql 34 | mysql-connector-java 35 | 36 | 37 | 38 | 39 | com.alibaba.cloud 40 | spring-cloud-starter-alibaba-nacos-discovery 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-maven-plugin 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /timeless-pay-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | timeless-order-pay-parent 7 | com.timeless 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | timeless-pay-server 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | 22 | com.timeless 23 | timeless-order-pay-api 24 | 1.0-SNAPSHOT 25 | 26 | 27 | com.baomidou 28 | mybatis-plus-boot-starter 29 | 30 | 31 | 32 | 33 | 34 | com.alipay.sdk 35 | alipay-sdk-java 36 | 37 | 38 | 39 | 40 | com.alibaba.cloud 41 | spring-cloud-starter-alibaba-nacos-discovery 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-maven-plugin 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/rabbitmq/OrderCancelConsumer.java: -------------------------------------------------------------------------------- 1 | package com.timeless.rabbitmq; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; 4 | import com.rabbitmq.client.Channel; 5 | import com.timeless.config.RabbitMQConfig; 6 | import com.timeless.constants.AppHttpCodeEnum; 7 | import com.timeless.domain.OrderInfo; 8 | import com.timeless.service.OrderInfoService; 9 | import com.timeless.utils.DateTimeUtils; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 12 | import org.springframework.amqp.support.AmqpHeaders; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.messaging.Message; 15 | import org.springframework.messaging.handler.annotation.Header; 16 | import org.springframework.stereotype.Component; 17 | 18 | import java.io.IOException; 19 | import java.nio.charset.StandardCharsets; 20 | import java.text.SimpleDateFormat; 21 | 22 | @Component 23 | @Slf4j 24 | public class OrderCancelConsumer { 25 | 26 | @Autowired 27 | private OrderInfoService orderInfoService; 28 | 29 | 30 | @RabbitListener(queues = RabbitMQConfig.DLX_QUEUE) 31 | public void listenerWithoutPlugins(OrderInfo orderInfo) { 32 | printMsg(orderInfo); 33 | 34 | } 35 | 36 | @RabbitListener(queues = RabbitMQConfig.PLUGINS_QUEUE) 37 | public void listenerWithPlugins(OrderInfo orderInfo) { 38 | printMsg(orderInfo); 39 | 40 | } 41 | 42 | private void printMsg(OrderInfo orderInfo) { 43 | log.error("订单超时取消中...." + DateTimeUtils.getCurrentDateTime() + " === " + orderInfo.getOrderNo()); 44 | 45 | OrderInfo orderInfo1 = orderInfoService.getById(orderInfo.getOrderNo()); 46 | if(orderInfo1 == null){ 47 | return; 48 | } 49 | if(AppHttpCodeEnum.CONTINUE_PAY.getMsg().equals(orderInfo1.getStatus())){ 50 | //取消订单 51 | orderInfo1.setStatus(AppHttpCodeEnum.ORDER_CANCEL.getMsg()); 52 | orderInfoService.updateById(orderInfo1); 53 | log.error("订单已取消......" + DateTimeUtils.getCurrentDateTime() + " === " + orderInfo.getOrderNo()); 54 | } 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/rabbitmq/RefundListener.java: -------------------------------------------------------------------------------- 1 | package com.timeless.rabbitmq; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; 4 | import com.timeless.config.RabbitMQConfig; 5 | import com.timeless.constants.AppHttpCodeEnum; 6 | import com.timeless.domain.OrderInfo; 7 | import com.timeless.domain.vo.RefundVo; 8 | import com.timeless.exception.SystemException; 9 | import com.timeless.feign.PayFeign; 10 | import com.timeless.result.ResponseResult; 11 | import com.timeless.service.OrderInfoService; 12 | import org.springframework.amqp.rabbit.annotation.Queue; 13 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 14 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.stereotype.Component; 17 | 18 | import java.util.Objects; 19 | 20 | @Component 21 | public class RefundListener { 22 | 23 | @Autowired 24 | private PayFeign payFeign; 25 | 26 | @Autowired 27 | private OrderInfoService orderInfoService; 28 | 29 | @Autowired 30 | private RabbitTemplate rabbitTemplate; 31 | 32 | /** 33 | * 这里其实可以先修改订单状态,再退款。(告诉用户,退款可能会有延迟。) 34 | * 不然,可能有种情况 ,用户退了款,还是看见订单处于“已付款”的状态。 35 | * @param refundVo 36 | */ 37 | @RabbitListener(queues = RabbitMQConfig.REFUND_QUEUE) 38 | public void handleRefund(RefundVo refundVo) { 39 | System.out.println("退款监听中........."); 40 | try { 41 | ResponseResult result = payFeign.refund(refundVo); 42 | if (Objects.isNull(result) || !result.getData()) { 43 | throw new SystemException(AppHttpCodeEnum.REFUND_FAIL); 44 | } 45 | orderInfoService.update(new UpdateWrapper() 46 | .set("status", AppHttpCodeEnum.DONE_REFUND.getMsg()) 47 | .eq("order_no", refundVo.getOutTradeNo())); 48 | System.out.println("订单已退款......"); 49 | } catch (Exception e) { 50 | e.printStackTrace(); 51 | // 处理退款失败的情况,例如发送通知邮件或者短信给相关人员 52 | rabbitTemplate.convertAndSend(RabbitMQConfig.REFUND_ERROR_QUEUE, refundVo); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /timeless-seckill-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | timeless-order-pay-parent 7 | com.timeless 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | timeless-seckill-server 13 | 14 | 15 | 8 16 | 8 17 | 18 | 19 | 20 | 21 | 22 | com.timeless 23 | timeless-order-pay-api 24 | 1.0-SNAPSHOT 25 | 26 | 27 | 28 | com.baomidou 29 | mybatis-plus-boot-starter 30 | 31 | 32 | 33 | mysql 34 | mysql-connector-java 35 | 36 | 37 | 38 | 39 | com.alibaba.cloud 40 | spring-cloud-starter-alibaba-nacos-discovery 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.aspectj 50 | aspectjweaver 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.springframework.boot 59 | spring-boot-maven-plugin 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/service/impl/SeckillProductServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.timeless.service.impl; 2 | 3 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 4 | import com.google.gson.Gson; 5 | import com.timeless.domain.Product; 6 | import com.timeless.domain.SeckillProduct; 7 | import com.timeless.domain.vo.SeckillProductVo; 8 | import com.timeless.feign.ProductFeign; 9 | import com.timeless.mapper.SeckillProductMapper; 10 | import com.timeless.result.ResponseResult; 11 | import com.timeless.service.SeckillProductService; 12 | import org.springframework.beans.BeanUtils; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Service; 15 | 16 | import java.util.Objects; 17 | 18 | /** 19 | * (SeckillProduct)表服务实现类 20 | * 21 | * @author makejava 22 | * @since 2023-05-28 11:51:05 23 | */ 24 | @Service("seckillProductService") 25 | public class SeckillProductServiceImpl extends ServiceImpl implements SeckillProductService { 26 | 27 | @Autowired 28 | private SeckillProductService seckillProductService; 29 | 30 | @Autowired 31 | private ProductFeign productFeign; 32 | 33 | @Override 34 | public SeckillProductVo getSeckkillProductVo(Long seckillId) { 35 | SeckillProductVo seckillProductVo = new SeckillProductVo(); 36 | 37 | // 得到seckillProduct 38 | SeckillProduct seckillProduct = seckillProductService.getById(seckillId); 39 | 40 | if(Objects.isNull(seckillProduct)){ 41 | return null; 42 | } 43 | 44 | // 得到productId 45 | Long productId = seckillProduct.getProductId(); 46 | 47 | // 远程调用,得到product 48 | ResponseResult res = productFeign.getProductByProductId(productId); 49 | Gson gson = new Gson(); 50 | String productJson = gson.toJson(res.getData()); 51 | Product product = gson.fromJson(productJson, Product.class); 52 | 53 | if(Objects.isNull(product)){ 54 | return null; 55 | } 56 | 57 | // 属性赋值 58 | BeanUtils.copyProperties(product , seckillProductVo); 59 | BeanUtils.copyProperties(seckillProduct , seckillProductVo); 60 | 61 | return seckillProductVo; 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /timeless-pay-server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8502 3 | 4 | spring: 5 | application: 6 | name: timeless-pay-server 7 | # cloud: 8 | # nacos: 9 | # discovery: 10 | # server-addr: VirtualIP:8848 11 | alipay: 12 | app_id: 2021000122674469 13 | merchant_private_key: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCTAT0pyVIMmEK2rxQFhX5cqKyVJZxA/A952ntyjAPOHPafDOalPc1wY5KBJD59idBp5HDQojYUgH4hLQBm0dU/+Fv9cQTkgIl47ZKPx+hU6zlZJfbyKUXVPhFQEMPddmvcgAfp/M+8cthbVt0Mjcp+x211+V0FPiqnY/fk5qXZjwthjICeEVmVVwtphDSK0UgzU5fhIcTjWxEYtznWhAWr7eYF2yD80jigWHQxiuf/er+HUf0gsCI/C1OxFUzlH9FBFFfBE+Lc4rQtSZ4kGsjFmpfHEPdYDwJ+xc5FISnOtb2htzp3wqR4Q1zvo2b9XSW+rFVxqNPcdxPC5BrgVGLvAgMBAAECggEAJb8fsWccRleiab4y6egJNkmpZvKtWJJgdu1+3T7Oi1IskVKCttNTuRQYPkjMMvf8J/ScczXzpPgJawNfIwemNjLTjBRFKVdH9WErTPgL1CJCK33wFuY6JDM3xtNHN8p8j7XgUli0DrN+kpWPVPXjlQIoPS62j+4SjcDBmmkPFPma8GKUZgGD3uNSNLnKrAorTMUviIYn9p4oR2gJ5EXIZk0KzhViWgI57SHIUx1nfstKnobTtP19aFyBdHgeUq5xxmVfHbBl/6qlOoxaS6ZQZA2U5WUlyQXQS4SlYC3m6tm581hTN4+J6D4DEx7MxA0cG4L3jyoB1tMC+1rTZsCoAQKBgQD7t2PDDrW0TpX52ZM6ZrFanP398qJp0f3AOz5Dko4YiK2yEZLZoM21uGYsC+Rr/ijg3RuqLPzNZFDFvsbdnO9CppIA8/f1O5jHAU+WdOCvvjLH8ITHRR+cFd+a7vJMiF4KeMXrdDYmyXroe2WQWluSpQJWoJGgqNrI/Jj9H8h9AQKBgQCVgauO4N6B6nSfht/jHJD/zD2up+J5e0YWbKZl3ITa4ZIXba1Yd8HCQvvfxiYcKXBlPVPsqg154m19UcHyvQMImNF7sPFGxpSjR9KGYEtB0uwGzVP5Vf+/t/28e29Ru2j4JgxJmZC6alOxOhmx1uIsUELtkEKxdGdil5feQrSv7wKBgEr9A1dk1nT0xHE+hEHtvgBErNYupnvn9zSBcbcnvfVJIpXd7mWvJhlw6d9NW6tgeEUlGczEwjteG0IN56i1zAGLJgvqooAIVSdUGKW2BAnXG8G8wZGr4hLZ4VeCv+RU688q06ulX0yG0XrY6BTtFkLFrcCo7TlzlFo0Bk/CXDQBAoGAFadWRbdkAsUGZySNwMMebS4TsPw33QhxukW6Q/6Qb3NESYhlFu8hbRVujZaRZnrKAJ/FS/3FPeYBnaj9wj6F9fTMQnH6QBn65Ts9zqCbxcjazpFmnDJMfGQVp/rowJm4NlisZuyK/bTwsjaMCinpUCm/x9ChGDqXzrvODy7yJJkCgYEA+fr22r7IX+CqFE7QlfMJ44ePAOQfx8zyN18RSzJzhUoS9W4iwnmL2x90Ak+XUC+7gm8HCL7GAR6KHLTO0miRAk6PNRyL2cStWg9MpXoZmxc13fbSXO2TT9dXyxifRnT1RYYqwiHzkY8Ni9oGC4jMzgugua4zEnC9yAbWXA/n1e8= 14 | alipay_public_key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuTGdCrOT/z0FM3wT68fHNWH38mGrkn9dgr/9xAUmm8puQYJr0yKhl4CGZLYeg/IC+w9sSVdj83kt+vp6lwrgNtSlNLf0gSucFMtRu5rzq3DgPgHZcsruLLaa/Gn4A0wzVL+XxCE5EGm/jYw9MIrP9tCiYKhLqqAlnAYlSBmslGi2SRj7Ch+BweKv1Ki1D2jeZC0O86rxNJKuHiCnk79RQ0rgvbB56gd8FQHNX8EVXq8xH/4OOu4v9IPtnMPBrhLNct/rMqo/JyOQI5QjnSk43yc5Xrox371E0Qrzf6zjoQPF0bzB+nzA3UwMuA82knvk7STLqJy21NBq7hT6r1gPIQIDAQAB 15 | sign_type: RSA2 16 | charset: utf-8 17 | gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do -------------------------------------------------------------------------------- /timeless-order-pay-api/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | timeless-order-pay-parent 7 | com.timeless 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | timeless-order-pay-api 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | org.projectlombok 23 | lombok 24 | 25 | 26 | 27 | com.baomidou 28 | mybatis-plus-boot-starter 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-web 34 | 35 | 36 | 37 | com.github.xiaoymin 38 | knife4j-spring-boot-starter 39 | 40 | 41 | 42 | 43 | org.springframework.cloud 44 | spring-cloud-starter-openfeign 45 | 46 | 47 | 48 | com.google.code.gson 49 | gson 50 | 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-amqp 55 | 56 | 57 | 58 | cn.hutool 59 | hutool-all 60 | 61 | 62 | 63 | org.springframework.boot 64 | spring-boot-starter-data-redis 65 | 66 | 67 | 68 | org.apache.commons 69 | commons-pool2 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/controller/OrderInfoController.java: -------------------------------------------------------------------------------- 1 | package com.timeless.controller; 2 | 3 | import com.timeless.constants.AppHttpCodeEnum; 4 | import com.timeless.domain.OrderInfo; 5 | import com.timeless.domain.vo.SeckillProductVo; 6 | import com.timeless.exception.SystemException; 7 | import com.timeless.result.ResponseResult; 8 | import com.timeless.service.OrderInfoService; 9 | import com.timeless.service.SeckillProductService; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.data.redis.core.RedisTemplate; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import java.util.List; 15 | import java.util.Objects; 16 | 17 | /** 18 | * @author timeless 19 | * @date 2023/5/28 13:38 20 | * @desciption: 21 | */ 22 | @RestController 23 | @RequestMapping("/orderInfo") 24 | public class OrderInfoController { 25 | 26 | @Autowired 27 | private RedisTemplate redisTemplate; 28 | 29 | @Autowired 30 | private SeckillProductService seckillProductService; 31 | 32 | @Autowired 33 | private OrderInfoService orderInfoService; 34 | 35 | /** 36 | * 秒杀商品 37 | * 38 | * @param seckillId 39 | * @return 40 | */ 41 | @GetMapping("/doSeckill") 42 | public ResponseResult doSeckill(@RequestParam("seckillId") Long seckillId, @RequestParam("expireTime") String expireTime) { 43 | 44 | OrderInfo orderInfo = orderInfoService.doSeckill(AppHttpCodeEnum.USERID, seckillId, expireTime); 45 | 46 | return ResponseResult.okResult(orderInfo); 47 | } 48 | 49 | @GetMapping("/getAllOrderInfo") 50 | public ResponseResult getAllOrderInfo() { 51 | return ResponseResult.okResult(orderInfoService.list()); 52 | } 53 | 54 | @GetMapping("/getOrderInfoByOrderNo/{orderNo}") 55 | public ResponseResult getOrderInfoByOrderNo(@PathVariable("orderNo") String orderNo) { 56 | return ResponseResult.okResult(orderInfoService.getById(orderNo)); 57 | } 58 | 59 | 60 | /** 61 | * @Description: 从购物车下单 62 | * @Date: 2023/6/17 14:36 63 | * @Author: timeless 64 | */ 65 | @PostMapping("/createOrderFromShopCart/{userId}") 66 | public ResponseResult createOrderFromShopCart(@PathVariable("userId") Long userId, 67 | @RequestParam("productId") List productId, 68 | @RequestParam("expireTime") String expireTime) { 69 | // TODO 判断商品是否存在于购物车中 70 | OrderInfo res = orderInfoService.createOrderFromShopCart(userId, productId, expireTime); 71 | return ResponseResult.okResult(res); 72 | } 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.4' 2 | services: 3 | order-nacos: 4 | container_name: order-nacos 5 | image: nacos/nacos-server:2.0.3 6 | build: 7 | context: ./nacos 8 | environment: 9 | - MODE=standalone 10 | volumes: 11 | - ./nacos/logs/:/home/nacos/logs 12 | - ./nacos/conf/application.properties:/home/nacos/conf/application.properties 13 | ports: 14 | - "8848:8848" 15 | - "9848:9848" 16 | - "9849:9849" 17 | privileged: true 18 | depends_on: 19 | order-mysql: 20 | condition: service_healthy 21 | # - order-mysql 22 | order-mysql: 23 | container_name: order-mysql 24 | image: mysql:8.0.26 25 | build: 26 | context: ./mysql 27 | ports: 28 | - "3306:3306" 29 | volumes: 30 | - ./mysql/conf:/etc/mysql/conf.d 31 | - ./mysql/logs:/logs 32 | - ./mysql/data:/var/lib/mysql 33 | - ./mysql/db:/docker-entrypoint-initdb.d/ 34 | command: [ 35 | 'mysqld', 36 | '--innodb-buffer-pool-size=80M', 37 | '--character-set-server=utf8mb4', 38 | '--collation-server=utf8mb4_unicode_ci', 39 | '--default-time-zone=+8:00', 40 | '--lower-case-table-names=1' 41 | ] 42 | privileged: true 43 | healthcheck: 44 | test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] 45 | timeout: 20s 46 | retries: 5 47 | environment: 48 | # MYSQL_DATABASE: 'order-cloud' 49 | MYSQL_ROOT_PASSWORD: 'root' 50 | order-redis: 51 | container_name: order-redis 52 | image: redis:6.0.8 53 | build: 54 | context: ./redis 55 | dockerfile: dockerfile 56 | ports: 57 | - "6379:6379" 58 | volumes: 59 | - ./redis/conf/redis.conf:/home/order/redis/redis.conf 60 | - ./redis/data:/data 61 | command: redis-server /home/order/redis/redis.conf 62 | order-product: 63 | container_name: order-product 64 | build: 65 | context: ./timeless-order/product 66 | dockerfile: dockerfile 67 | ports: 68 | - "8500:8500" 69 | depends_on: 70 | - order-redis 71 | - order-mysql 72 | links: 73 | - order-redis 74 | - order-mysql 75 | order-seckill: 76 | container_name: order-seckill 77 | build: 78 | context: ./timeless-order/seckill 79 | dockerfile: dockerfile 80 | ports: 81 | - "8501:8501" 82 | depends_on: 83 | - order-redis 84 | - order-mysql 85 | - order-product 86 | links: 87 | - order-redis 88 | - order-mysql 89 | - order-product 90 | order-pay: 91 | container_name: order-pay 92 | build: 93 | context: ./timeless-order/pay 94 | dockerfile: dockerfile 95 | ports: 96 | - "8502:8502" 97 | depends_on: 98 | - order-redis 99 | - order-mysql 100 | - order-product 101 | - order-seckill 102 | links: 103 | - order-redis 104 | - order-mysql 105 | - order-product 106 | - order-seckill 107 | -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/java/com/timeless/utils/IdGenerateUtil.java: -------------------------------------------------------------------------------- 1 | package com.timeless.utils; 2 | 3 | /** 4 | * twitter 的分布式环境全局唯一id算法 5 | */ 6 | public class IdGenerateUtil { 7 | private long workerId; 8 | private long datacenterId; 9 | private long sequence = 0L; 10 | private long twepoch = 1288834974657L; 11 | private long workerIdBits = 5L; 12 | private long datacenterIdBits = 5L; 13 | private long maxWorkerId = -1L ^ (-1L << workerIdBits); 14 | private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); 15 | private long sequenceBits = 12L; 16 | private long workerIdShift = sequenceBits; 17 | private long datacenterIdShift = sequenceBits + workerIdBits; 18 | private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; 19 | private long sequenceMask = -1L ^ (-1L << sequenceBits); //4095 20 | private long lastTimestamp = -1L; 21 | 22 | private static class IdGenHolder { 23 | private static final IdGenerateUtil instance = new IdGenerateUtil(); 24 | } 25 | 26 | public static IdGenerateUtil get() { 27 | return IdGenHolder.instance; 28 | } 29 | 30 | public IdGenerateUtil() { 31 | this(0L, 0L); 32 | } 33 | 34 | public IdGenerateUtil(long workerId, long datacenterId) { 35 | if (workerId > maxWorkerId || workerId < 0) { 36 | throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); 37 | } 38 | if (datacenterId > maxDatacenterId || datacenterId < 0) { 39 | throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); 40 | } 41 | this.workerId = workerId; 42 | this.datacenterId = datacenterId; 43 | } 44 | 45 | public synchronized long nextId() { 46 | long timestamp = timeGen(); 47 | if (timestamp < lastTimestamp) { 48 | throw new RuntimeException(String.format( 49 | "Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); 50 | } 51 | //如果上次生成时间和当前时间相同,在同一毫秒内 52 | if (lastTimestamp == timestamp) { 53 | sequence = (sequence + 1) & sequenceMask; 54 | if (sequence == 0) { 55 | timestamp = tilNextMillis(lastTimestamp); 56 | } 57 | } else { 58 | sequence = 0L; 59 | } 60 | lastTimestamp = timestamp; 61 | return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) 62 | | (workerId << workerIdShift) | sequence; 63 | } 64 | 65 | protected long tilNextMillis(long lastTimestamp) { 66 | long timestamp = timeGen(); 67 | while (timestamp <= lastTimestamp) { 68 | timestamp = timeGen(); 69 | } 70 | return timestamp; 71 | } 72 | 73 | protected long timeGen() { 74 | return System.currentTimeMillis(); 75 | } 76 | 77 | 78 | } -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/controller/ShopCartController.java: -------------------------------------------------------------------------------- 1 | package com.timeless.controller; 2 | 3 | import com.baomidou.mybatisplus.core.toolkit.Wrappers; 4 | import com.google.gson.Gson; 5 | import com.timeless.constants.AppHttpCodeEnum; 6 | import com.timeless.domain.Product; 7 | import com.timeless.domain.ShopCart; 8 | import com.timeless.exception.SystemException; 9 | import com.timeless.feign.ProductFeign; 10 | import com.timeless.result.ResponseResult; 11 | import com.timeless.service.ShopCartService; 12 | import com.timeless.utils.SnowFlakeUtil; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.web.bind.annotation.*; 15 | 16 | import java.sql.Wrapper; 17 | import java.util.List; 18 | import java.util.Objects; 19 | 20 | /** 21 | * @author timeless 22 | * @create 2023-06-17 14:54 23 | */ 24 | @RestController 25 | @RequestMapping("/ShopCartController") 26 | public class ShopCartController { 27 | 28 | @Autowired 29 | private ShopCartService shopCartService; 30 | 31 | @Autowired 32 | private ProductFeign productFeign; 33 | 34 | /** 35 | * @Description: 查询我的购物车的所有商品 36 | * @Date: 2023/6/17 14:56 37 | * @Author: timeless 38 | */ 39 | @GetMapping("/getAllMyCartProducts/{userId}") 40 | public ResponseResult> getAllMyCartProducts(@PathVariable("userId") Long userId) { 41 | List shopCarts = shopCartService.list(Wrappers.lambdaQuery().eq(ShopCart::getUserId, userId)); 42 | return ResponseResult.okResult(shopCarts); 43 | } 44 | 45 | /** 46 | * @Description: 向购物车添加/修改商品 47 | * @Date: 2023/6/17 15:04 48 | * @Author: timeless 49 | */ 50 | @PostMapping("/addOrUpdateProductToShopCart") 51 | public ResponseResult addProductToShopCart(@RequestBody ShopCart shopCart) { 52 | // 判断商品是否在商品表中 53 | ResponseResult responseResult = productFeign.getProductByProductId(shopCart.getProductId()); 54 | Gson gson = new Gson(); 55 | String productJson = gson.toJson(responseResult.getData()); 56 | Product product = gson.fromJson(productJson, Product.class); 57 | 58 | if(Objects.isNull(responseResult) || Objects.isNull(product)){ 59 | throw new SystemException(AppHttpCodeEnum.PRODUCT_NOT_EXIST); 60 | } 61 | 62 | List shopCarts = shopCartService.list(Wrappers.lambdaQuery().eq(ShopCart::getUserId, shopCart.getUserId())); 63 | ShopCart result = shopCarts.isEmpty() ? null : shopCarts.get(0); 64 | shopCart.setShopCartId(Objects.isNull(result) ? SnowFlakeUtil.getNextId() : result.getShopCartId().isEmpty() ? SnowFlakeUtil.getNextId() : result.getShopCartId()); 65 | shopCart.setProductPrice(product.getProductPrice()); 66 | boolean saveOrUpdate = shopCartService 67 | .saveOrUpdate( 68 | shopCart, 69 | Wrappers.lambdaUpdate() 70 | .eq(ShopCart::getUserId, shopCart.getUserId()) 71 | .eq(ShopCart::getProductId, shopCart.getProductId())); 72 | return ResponseResult.okResult(saveOrUpdate); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/java/com/timeless/result/ResponseResult.java: -------------------------------------------------------------------------------- 1 | package com.timeless.result; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.timeless.constants.AppHttpCodeEnum; 5 | 6 | import java.io.Serializable; 7 | 8 | @JsonInclude(JsonInclude.Include.NON_NULL) 9 | public class ResponseResult implements Serializable { 10 | private Integer code; 11 | private String msg; 12 | private T data; 13 | 14 | public ResponseResult() { 15 | this.code = AppHttpCodeEnum.SUCCESS.getCode(); 16 | this.msg = AppHttpCodeEnum.SUCCESS.getMsg(); 17 | } 18 | 19 | public ResponseResult(Integer code, T data) { 20 | this.code = code; 21 | this.data = data; 22 | } 23 | 24 | public ResponseResult(Integer code, String msg, T data) { 25 | this.code = code; 26 | this.msg = msg; 27 | this.data = data; 28 | } 29 | 30 | public ResponseResult(Integer code, String msg) { 31 | this.code = code; 32 | this.msg = msg; 33 | } 34 | 35 | public static ResponseResult errorResult(int code, String msg) { 36 | ResponseResult result = new ResponseResult(); 37 | return result.error(code, msg); 38 | } 39 | public static ResponseResult okResult() { 40 | ResponseResult result = new ResponseResult(); 41 | return result; 42 | } 43 | public static ResponseResult okResult(int code, String msg) { 44 | ResponseResult result = new ResponseResult(); 45 | return result.ok(code, null, msg); 46 | } 47 | 48 | public static ResponseResult okResult(Object data) { 49 | ResponseResult result = setAppHttpCodeEnum(AppHttpCodeEnum.SUCCESS, AppHttpCodeEnum.SUCCESS.getMsg()); 50 | if(data!=null) { 51 | result.setData(data); 52 | } 53 | return result; 54 | } 55 | 56 | public static ResponseResult errorResult(AppHttpCodeEnum enums){ 57 | return setAppHttpCodeEnum(enums,enums.getMsg()); 58 | } 59 | 60 | public static ResponseResult errorResult(AppHttpCodeEnum enums, String msg){ 61 | return setAppHttpCodeEnum(enums,msg); 62 | } 63 | 64 | public static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums){ 65 | return okResult(enums.getCode(),enums.getMsg()); 66 | } 67 | 68 | private static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums, String msg){ 69 | return okResult(enums.getCode(),msg); 70 | } 71 | 72 | public ResponseResult error(Integer code, String msg) { 73 | this.code = code; 74 | this.msg = msg; 75 | return this; 76 | } 77 | 78 | public ResponseResult error(AppHttpCodeEnum appHttpCodeEnum) { 79 | this.code = appHttpCodeEnum.getCode(); 80 | this.msg = appHttpCodeEnum.getMsg(); 81 | return this; 82 | } 83 | 84 | public ResponseResult ok(Integer code, T data) { 85 | this.code = code; 86 | this.data = data; 87 | return this; 88 | } 89 | 90 | public ResponseResult ok(Integer code, T data, String msg) { 91 | this.code = code; 92 | this.data = data; 93 | this.msg = msg; 94 | return this; 95 | } 96 | 97 | public ResponseResult ok(T data) { 98 | this.data = data; 99 | return this; 100 | } 101 | 102 | public Integer getCode() { 103 | return code; 104 | } 105 | 106 | public void setCode(Integer code) { 107 | this.code = code; 108 | } 109 | 110 | public String getMsg() { 111 | return msg; 112 | } 113 | 114 | public void setMsg(String msg) { 115 | this.msg = msg; 116 | } 117 | 118 | public T getData() { 119 | return data; 120 | } 121 | 122 | public void setData(T data) { 123 | this.data = data; 124 | } 125 | 126 | 127 | 128 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.timeless 8 | timeless-order-pay-parent 9 | pom 10 | 1.0-SNAPSHOT 11 | 12 | timeless-order-pay-api 13 | timeless-product-server 14 | timeless-pay-server 15 | timeless-seckill-server 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot-starter-parent 21 | 2.3.2.RELEASE 22 | 23 | 24 | 25 | Hoxton.SR8 26 | 2.2.5.RELEASE 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-web 37 | 2.3.9.RELEASE 38 | 39 | 40 | 41 | 42 | 43 | org.springframework.cloud 44 | spring-cloud-dependencies 45 | ${spring-cloud.version} 46 | pom 47 | import 48 | 49 | 50 | 51 | com.alibaba.cloud 52 | spring-cloud-alibaba-dependencies 53 | ${spring-cloud-alibaba.version} 54 | pom 55 | import 56 | 57 | 58 | 59 | mysql 60 | mysql-connector-java 61 | 8.0.28 62 | 63 | 64 | 65 | com.baomidou 66 | mybatis-plus-boot-starter 67 | 3.5.2 68 | 69 | 70 | 71 | com.github.xiaoymin 72 | knife4j-spring-boot-starter 73 | 2.0.9 74 | 75 | 76 | 77 | org.projectlombok 78 | lombok 79 | 1.18.24 80 | 81 | 82 | 83 | com.google.code.gson 84 | gson 85 | 2.8.2 86 | 87 | 88 | 89 | com.alipay.sdk 90 | alipay-sdk-java 91 | 4.10.170.ALL 92 | 93 | 94 | 95 | org.springframework.boot 96 | spring-boot-starter-amqp 97 | 2.6.7 98 | 99 | 100 | 101 | 102 | cn.hutool 103 | hutool-all 104 | 5.4.0 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /timeless-pay-server/src/main/java/com/timeless/controller/PayController.java: -------------------------------------------------------------------------------- 1 | package com.timeless.controller; 2 | 3 | import com.alipay.api.AlipayApiException; 4 | import com.alipay.api.AlipayClient; 5 | import com.alipay.api.BatchAlipayRequest; 6 | import com.alipay.api.internal.util.AlipaySignature; 7 | import com.alipay.api.request.AlipayTradePagePayRequest; 8 | import com.alipay.api.request.AlipayTradeRefundRequest; 9 | import com.alipay.api.request.AlipayTradeWapPayRequest; 10 | import com.alipay.api.response.AlipayTradeRefundResponse; 11 | import com.timeless.config.AlipayConfig; 12 | import com.timeless.config.AlipayProperties; 13 | import com.timeless.constants.AppHttpCodeEnum; 14 | import com.timeless.domain.OrderInfo; 15 | import com.timeless.domain.vo.PayVo; 16 | import com.timeless.domain.vo.RefundVo; 17 | import com.timeless.exception.SystemException; 18 | import com.timeless.feign.OrderInfoFeign; 19 | import com.timeless.result.ResponseResult; 20 | import com.timeless.utils.DateTimeUtils; 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.web.bind.annotation.*; 23 | 24 | import java.util.Map; 25 | 26 | /** 27 | * @author timeless 28 | * @date 2023/5/28 16:27 29 | * @desciption: 30 | */ 31 | @RestController 32 | @RequestMapping("/aliPay") 33 | public class PayController { 34 | 35 | @Autowired 36 | private AlipayClient alipayClient; 37 | 38 | @Autowired 39 | private AlipayProperties alipayProperties; 40 | 41 | @Autowired 42 | private OrderInfoFeign orderInfoFeign; 43 | 44 | /** 45 | * 46 | * @param payVo 47 | * @return 这个返回的是html界面,这个界面要填写支付宝账号密码。 48 | * @throws AlipayApiException 49 | */ 50 | @RequestMapping("/payOnline") 51 | public ResponseResult payOnline(@RequestBody PayVo payVo) throws AlipayApiException { 52 | 53 | // 判断订单状态 , 如果不是待付款,就返回失败 54 | ResponseResult responseResult = orderInfoFeign.getOrderInfoByOrderNo(payVo.getOutTradeNo()); 55 | OrderInfo orderInfo = responseResult.getData(); 56 | String status = orderInfo.getStatus(); 57 | 58 | if(!AppHttpCodeEnum.CONTINUE_PAY.getMsg().equals(status)){ 59 | return ResponseResult.okResult(status); 60 | // throw new SystemException(AppHttpCodeEnum.PAY_FAIL); 61 | } 62 | 63 | // 设置请求参数 64 | AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest(); 65 | // 同步回调地址 66 | alipayRequest.setReturnUrl(payVo.getReturnUrl()); 67 | // 异步回调地址 68 | alipayRequest.setNotifyUrl(payVo.getNotifyUrl()); 69 | 70 | // 订单过期时间 71 | // String expireTime = DateTimeUtils.getCurrentDateTimePlusOneMinute(1L); 72 | // System.out.println(expireTime); 73 | 74 | alipayRequest.setBizContent("{\"out_trade_no\":\""+ payVo.getOutTradeNo() +"\"," 75 | + "\"total_amount\":\""+ payVo.getTotalAmount() +"\"," 76 | + "\"subject\":\""+ payVo.getSubject() +"\"," 77 | + "\"body\":\""+ payVo.getBody() +"\"," 78 | + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}"); 79 | // + "\"timeout_express\":\"1m\"}"); // 设置订单过期时间为1分钟 80 | // + "\"time_expire\":\"" + expireTime + "\"}"); // 设置订单过期时间 81 | String html = alipayClient.pageExecute(alipayRequest).getBody(); 82 | BatchAlipayRequest batchAlipayRequest = new BatchAlipayRequest(); 83 | return ResponseResult.okResult(html); 84 | } 85 | 86 | @RequestMapping("/rsaCheckV1") 87 | ResponseResult rsaCheckV1(@RequestParam Map params) throws AlipayApiException { 88 | boolean signVerified = AlipaySignature.rsaCheckV1(params, 89 | alipayProperties.getAlipayPublicKey(), 90 | alipayProperties.getCharset(), 91 | alipayProperties.getSignType()); //调用SDK验证签名 92 | return ResponseResult.okResult(signVerified); 93 | 94 | } 95 | 96 | @RequestMapping("/refund") 97 | ResponseResult refund(@RequestBody RefundVo refundVo) throws AlipayApiException { 98 | AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest(); 99 | alipayRequest.setBizContent("{\"out_trade_no\":\""+ refundVo.getOutTradeNo() +"\"," 100 | + "\"trade_no\":\"\"," 101 | + "\"refund_amount\":\""+ refundVo.getRefundAmount() +"\"," 102 | + "\"refund_reason\":\""+ refundVo.getRefundReason() +"\"," 103 | + "\"out_request_no\":\"\"}"); 104 | 105 | //请求 106 | AlipayTradeRefundResponse res = alipayClient.execute(alipayRequest); 107 | return ResponseResult.okResult(res.isSuccess()); 108 | } 109 | 110 | @GetMapping("/testDocker") 111 | public ResponseResult testDocker(){ 112 | return ResponseResult.okResult(); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /sql/timeless_product.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | 4 | Source Server : 1 5 | Source Server Type : MySQL 6 | Source Server Version : 80030 7 | Source Host : localhost:3306 8 | Source Schema : timeless_product 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 80030 12 | File Encoding : 65001 13 | 14 | Date: 10/06/2023 21:54:24 15 | */ 16 | 17 | DROP DATABASE IF EXISTS `timeless_product`; 18 | 19 | CREATE DATABASE `timeless_product` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; 20 | 21 | USE `timeless_product`; 22 | 23 | SET NAMES utf8mb4; 24 | SET FOREIGN_KEY_CHECKS = 0; 25 | 26 | -- ---------------------------- 27 | -- Table structure for t_product 28 | -- ---------------------------- 29 | DROP TABLE IF EXISTS `t_product`; 30 | CREATE TABLE `t_product` ( 31 | `id` bigint(0) NOT NULL AUTO_INCREMENT, 32 | `product_name` varchar(200) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, 33 | `product_title` varchar(200) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, 34 | `product_detail` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL, 35 | `product_price` decimal(10, 2) NULL DEFAULT NULL, 36 | PRIMARY KEY (`id`) USING BTREE 37 | ) ENGINE = InnoDB AUTO_INCREMENT = 42 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic; 38 | 39 | -- ---------------------------- 40 | -- Records of t_product 41 | -- ---------------------------- 42 | INSERT INTO `t_product` VALUES (23, '华为nova7pro 8G+128G全网通', '麒麟985芯片,卖一台少一台,华为直供货源,全新原封正品【赠】原装碎屏险+2年保修+运费险+液态硅胶壳+贴膜', '华为nova7pro 5G手机 7号色 8G+128G全网通', 4399.00); 43 | INSERT INTO `t_product` VALUES (24, 'VAIO 11代新品笔记本', ' VAIO FH14 侍14 11代酷睿 14英寸 1.4Kg 4G独显 高性能轻薄笔记本电脑(i5 16G 512G SSD GTX1650 FHD)铂金银', ' VAIO FH14 侍14 11代酷睿 14英寸 1.4Kg 4G独显 高性能轻薄笔记本电脑(i5 16G 512G SSD GTX1650 FHD)铂金银', 4999.00); 44 | INSERT INTO `t_product` VALUES (25, '独牙(DUYA)可充电无线蓝牙鼠标', '三模办公静音锂电池鼠标 2.4G蓝牙5.0笔记本IPAD电脑平板通用 M203太空银', '三模办公静音锂电池鼠标 2.4G蓝牙5.0笔记本IPAD电脑平板通用 M203太空银', 49.00); 45 | INSERT INTO `t_product` VALUES (26, '玉翊 新疆和田玉', '观音吊坠男羊脂白玉佛玉坠女本命佛挂件 羊脂玉佛小号【25*26*6】', '观音吊坠男羊脂白玉佛玉坠女本命佛挂件 羊脂玉佛小号【25*26*6】', 1399.00); 46 | INSERT INTO `t_product` VALUES (27, '雅鹿品牌 加绒加厚 连帽夹克男', '雅鹿(YALU)【加绒可选】外套男 秋冬新款男装连帽夹克外套男士衣服男装外套12.3 601浅蓝米白(常规款) L码', '雅鹿(YALU)【加绒可选】外套男 秋冬新款男装连帽夹克外套男士衣服男装外套12.3 601浅蓝米白(常规款) L码', 199.00); 47 | INSERT INTO `t_product` VALUES (28, '创维10公斤大容量变频滚筒', '创维(SKYWORTH) 10公斤 滚筒洗衣机全自动 家用一级变频 除菌除螨 桶自洁 15分快洗 XQG100-B15LB', '创维(SKYWORTH) 10公斤 滚筒洗衣机全自动 家用一级变频 除菌除螨 桶自洁 15分快洗 XQG100-B15LB', 2399.00); 48 | INSERT INTO `t_product` VALUES (29, '夏普70英寸4K+HDR10液晶电视', '夏普(SHARP)70A5RD 70英寸日本原装面板4K超清网络智能液晶平板电视', '夏普(SHARP)70A5RD 70英寸日本原装面板4K超清网络智能液晶平板电视', 6399.00); 49 | INSERT INTO `t_product` VALUES (30, 'Redmi K30 5G 6GB+128GB紫玉幻境', 'Redmi K30 5G双模 120Hz流速屏 骁龙765G 前置挖孔双摄 索尼6400万后置四摄 30W快充 6GB+128GB 紫玉幻境 游戏智能手机 小米 红米', 'Redmi K30 5G双模 120Hz流速屏 骁龙765G 前置挖孔双摄 索尼6400万后置四摄 30W快充 6GB+128GB 紫玉幻境 游戏智能手机 小米 红米', 1699.00); 50 | INSERT INTO `t_product` VALUES (31, 'Apple Watch S5 GPS款 40毫米', 'Apple Watch Series 5智能手表(GPS款 40毫米深空灰色铝金属表壳 黑色运动型表带 MWV82CH/A)', '【事事拿手,轻松入手!】【一站式以旧watch换新享额外200-500补贴优惠!】【新品再享30天试用!】库存紧张,马上抢购!', 3199.00); 51 | INSERT INTO `t_product` VALUES (32, '荣耀手表GS Pro 碳石黑', '荣耀手表GS Pro 碳石黑 25天续航 华为麒麟A1芯 103种运动模式 军规 智能语音蓝牙通话 50米防水 心率血氧GPS', '荣耀手表GS Pro 碳石黑 25天续航 华为麒麟A1芯 103种运动模式 军规 智能语音蓝牙通话 50米防水 心率血氧GPS', 1599.00); 52 | INSERT INTO `t_product` VALUES (33, '仓鼠太空舱暖手宝充电宝二合一', '中意礼暖手宝充电暖宝宝电暖热水袋圣诞节礼物生日礼物送女友闺蜜女朋友情人节礼物送老婆结婚纪念日圣诞礼物 太空仓鼠暖手宝', '【冬日优选,超温暖】超萌仓鼠形象,女生一见倾心,5秒快速升温,四档恒温,立体公仔,持续暖手3-6小时', 299.00); 53 | INSERT INTO `t_product` VALUES (34, '【价同12.12】美的60升电热爆款', '美的(Midea)60升电热水器2100W变频速热健康洗 智能APP控制 一级节能自动关机F6021-JA1(HEY)', '【12月厨卫开门红】冰点价949!限时秒杀! 【2100W变频速热!健康沐浴!一级节能】 【手机APP控制!8年包修】鸿蒙新品上市', 1999.00); 54 | INSERT INTO `t_product` VALUES (35, '麦瑞克航空专利减震家用跑步机', '【航空专利减震】【送上楼包安装】麦瑞克Merach跑步机家用静音折叠走步机运动健身器材 【推荐款】10.1吋彩屏多功能/航空减震/带按摩机', '【12.4日开门红】抢伴价、抢八折,爆款前一小时限时直降!更有华为P40pro+、爆款椭圆机等抽不停!白条免息运费险等福利领取!', 3999.00); 55 | INSERT INTO `t_product` VALUES (36, '华为畅享20 5G手机【大屏幕/大电池】 绮境森林 6+128G 全网通', '华为畅享20 5G手机【大屏幕/大电池】 绮境森林 6+128G 全网通', '咨询客服享优惠】AI三摄闪拍,5000mAh电池【另有畅享20plus点此】', 2299.00); 56 | INSERT INTO `t_product` VALUES (37, '三星Galaxy Note20 Ultra 5G手机游戏手机 12GB+256GB 迷雾金', '三星Galaxy Note20 Ultra 5G(SM-N9860)S Pen&三星笔记 120Hz自适应屏幕 5G手机游戏手机 12GB+256GB 迷雾金', '白条12期免息!下单赠价值1199元BudsLive耳机!全场至高优惠4000元!', 9199.00); 57 | INSERT INTO `t_product` VALUES (38, '洁滔(GILLTAO)德国三档强力增压淋浴花洒喷头', '洁滔(GILLTAO)德国三档强力增压淋浴花洒喷头手持沐浴洗澡淋雨大出水可拆洗万向旋转莲蓬头套装 三挡增压止水瓷白款', '12.02号下午2点掌上秒杀,瓷白单喷头仅需19元,两件再享9折,上期秒杀2小时3500单抢光,欲购从速。三挡增压,一键止水,可拆洗,头部万向旋转,不增压包退。', 49.00); 58 | INSERT INTO `t_product` VALUES (39, 'OPPO K7x 新品双模5G手机 90Hz电竞屏 智能拍照游戏 ', 'OPPO K7x 新品双模5G手机 90Hz电竞屏 智能拍照游戏 长续航手机oppok7x 黑镜 6+128G【现货速发】 全网通5G 闪充套装', '【现货速发,到手1499起】购机送蓝牙佴机+1年屏碎险+2年质保+晒单20+会员积分~5G硬核新品K7x赠蓝牙佴机', 1599.00); 59 | INSERT INTO `t_product` VALUES (40, '松下5款吸嘴家用大吸力吸尘器', '松下(panasonic)家用吸尘器大吸力小型地毯床上办公室强力除尘吸尘器宠物家庭适用 MC-WLC87字母负离子吸嘴', '【锁定12.3日10点】限时好价659!错过12.12都没有! 【团购咨询客服】可领取惊喜出厂价哦! 【全国联保,售后无忧】180以换代修!立即加购', 1499.00); 60 | INSERT INTO `t_product` VALUES (41, '阿玛尼商务钢带男士时尚石英表', '【品牌授权】Emporio Armani 阿玛尼手表 欧美表 商务男士腕表 皮带钢带男表石英表 AR1819', '【大牌秒杀】爆款男士钢带表AR819到手价1300更多优惠请咨询再线客服', 2990.00); 61 | INSERT INTO `t_product` VALUES (42, '联想拯救者12GB+256GB 炽焰战甲', '联想拯救者电竞手机Pro 12GB+256GB 骁龙865 Plus 双液冷散热 144Hz电竞屏 双模5G游戏手机 炽焰战甲', '【专享2500元VIP特权】高通骁龙865plus游戏高手的选择,双模5G,144Hz电竞屏,双液冷散热,晒单得50元E卡!至尊透明版', 4199.00); 62 | 63 | SET FOREIGN_KEY_CHECKS = 1; 64 | -------------------------------------------------------------------------------- /docker/mysql/db/timeless_product.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | 4 | Source Server : 1 5 | Source Server Type : MySQL 6 | Source Server Version : 80030 7 | Source Host : localhost:3306 8 | Source Schema : timeless_product 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 80030 12 | File Encoding : 65001 13 | 14 | Date: 10/06/2023 21:54:24 15 | */ 16 | 17 | DROP DATABASE IF EXISTS `timeless_product`; 18 | 19 | CREATE DATABASE `timeless_product` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; 20 | 21 | USE `timeless_product`; 22 | 23 | SET NAMES utf8mb4; 24 | SET FOREIGN_KEY_CHECKS = 0; 25 | 26 | -- ---------------------------- 27 | -- Table structure for t_product 28 | -- ---------------------------- 29 | DROP TABLE IF EXISTS `t_product`; 30 | CREATE TABLE `t_product` ( 31 | `id` bigint(0) NOT NULL AUTO_INCREMENT, 32 | `product_name` varchar(200) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, 33 | `product_title` varchar(200) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, 34 | `product_detail` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL, 35 | `product_price` decimal(10, 2) NULL DEFAULT NULL, 36 | PRIMARY KEY (`id`) USING BTREE 37 | ) ENGINE = InnoDB AUTO_INCREMENT = 42 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic; 38 | 39 | -- ---------------------------- 40 | -- Records of t_product 41 | -- ---------------------------- 42 | INSERT INTO `t_product` VALUES (23, '华为nova7pro 8G+128G全网通', '麒麟985芯片,卖一台少一台,华为直供货源,全新原封正品【赠】原装碎屏险+2年保修+运费险+液态硅胶壳+贴膜', '华为nova7pro 5G手机 7号色 8G+128G全网通', 4399.00); 43 | INSERT INTO `t_product` VALUES (24, 'VAIO 11代新品笔记本', ' VAIO FH14 侍14 11代酷睿 14英寸 1.4Kg 4G独显 高性能轻薄笔记本电脑(i5 16G 512G SSD GTX1650 FHD)铂金银', ' VAIO FH14 侍14 11代酷睿 14英寸 1.4Kg 4G独显 高性能轻薄笔记本电脑(i5 16G 512G SSD GTX1650 FHD)铂金银', 4999.00); 44 | INSERT INTO `t_product` VALUES (25, '独牙(DUYA)可充电无线蓝牙鼠标', '三模办公静音锂电池鼠标 2.4G蓝牙5.0笔记本IPAD电脑平板通用 M203太空银', '三模办公静音锂电池鼠标 2.4G蓝牙5.0笔记本IPAD电脑平板通用 M203太空银', 49.00); 45 | INSERT INTO `t_product` VALUES (26, '玉翊 新疆和田玉', '观音吊坠男羊脂白玉佛玉坠女本命佛挂件 羊脂玉佛小号【25*26*6】', '观音吊坠男羊脂白玉佛玉坠女本命佛挂件 羊脂玉佛小号【25*26*6】', 1399.00); 46 | INSERT INTO `t_product` VALUES (27, '雅鹿品牌 加绒加厚 连帽夹克男', '雅鹿(YALU)【加绒可选】外套男 秋冬新款男装连帽夹克外套男士衣服男装外套12.3 601浅蓝米白(常规款) L码', '雅鹿(YALU)【加绒可选】外套男 秋冬新款男装连帽夹克外套男士衣服男装外套12.3 601浅蓝米白(常规款) L码', 199.00); 47 | INSERT INTO `t_product` VALUES (28, '创维10公斤大容量变频滚筒', '创维(SKYWORTH) 10公斤 滚筒洗衣机全自动 家用一级变频 除菌除螨 桶自洁 15分快洗 XQG100-B15LB', '创维(SKYWORTH) 10公斤 滚筒洗衣机全自动 家用一级变频 除菌除螨 桶自洁 15分快洗 XQG100-B15LB', 2399.00); 48 | INSERT INTO `t_product` VALUES (29, '夏普70英寸4K+HDR10液晶电视', '夏普(SHARP)70A5RD 70英寸日本原装面板4K超清网络智能液晶平板电视', '夏普(SHARP)70A5RD 70英寸日本原装面板4K超清网络智能液晶平板电视', 6399.00); 49 | INSERT INTO `t_product` VALUES (30, 'Redmi K30 5G 6GB+128GB紫玉幻境', 'Redmi K30 5G双模 120Hz流速屏 骁龙765G 前置挖孔双摄 索尼6400万后置四摄 30W快充 6GB+128GB 紫玉幻境 游戏智能手机 小米 红米', 'Redmi K30 5G双模 120Hz流速屏 骁龙765G 前置挖孔双摄 索尼6400万后置四摄 30W快充 6GB+128GB 紫玉幻境 游戏智能手机 小米 红米', 1699.00); 50 | INSERT INTO `t_product` VALUES (31, 'Apple Watch S5 GPS款 40毫米', 'Apple Watch Series 5智能手表(GPS款 40毫米深空灰色铝金属表壳 黑色运动型表带 MWV82CH/A)', '【事事拿手,轻松入手!】【一站式以旧watch换新享额外200-500补贴优惠!】【新品再享30天试用!】库存紧张,马上抢购!', 3199.00); 51 | INSERT INTO `t_product` VALUES (32, '荣耀手表GS Pro 碳石黑', '荣耀手表GS Pro 碳石黑 25天续航 华为麒麟A1芯 103种运动模式 军规 智能语音蓝牙通话 50米防水 心率血氧GPS', '荣耀手表GS Pro 碳石黑 25天续航 华为麒麟A1芯 103种运动模式 军规 智能语音蓝牙通话 50米防水 心率血氧GPS', 1599.00); 52 | INSERT INTO `t_product` VALUES (33, '仓鼠太空舱暖手宝充电宝二合一', '中意礼暖手宝充电暖宝宝电暖热水袋圣诞节礼物生日礼物送女友闺蜜女朋友情人节礼物送老婆结婚纪念日圣诞礼物 太空仓鼠暖手宝', '【冬日优选,超温暖】超萌仓鼠形象,女生一见倾心,5秒快速升温,四档恒温,立体公仔,持续暖手3-6小时', 299.00); 53 | INSERT INTO `t_product` VALUES (34, '【价同12.12】美的60升电热爆款', '美的(Midea)60升电热水器2100W变频速热健康洗 智能APP控制 一级节能自动关机F6021-JA1(HEY)', '【12月厨卫开门红】冰点价949!限时秒杀! 【2100W变频速热!健康沐浴!一级节能】 【手机APP控制!8年包修】鸿蒙新品上市', 1999.00); 54 | INSERT INTO `t_product` VALUES (35, '麦瑞克航空专利减震家用跑步机', '【航空专利减震】【送上楼包安装】麦瑞克Merach跑步机家用静音折叠走步机运动健身器材 【推荐款】10.1吋彩屏多功能/航空减震/带按摩机', '【12.4日开门红】抢伴价、抢八折,爆款前一小时限时直降!更有华为P40pro+、爆款椭圆机等抽不停!白条免息运费险等福利领取!', 3999.00); 55 | INSERT INTO `t_product` VALUES (36, '华为畅享20 5G手机【大屏幕/大电池】 绮境森林 6+128G 全网通', '华为畅享20 5G手机【大屏幕/大电池】 绮境森林 6+128G 全网通', '咨询客服享优惠】AI三摄闪拍,5000mAh电池【另有畅享20plus点此】', 2299.00); 56 | INSERT INTO `t_product` VALUES (37, '三星Galaxy Note20 Ultra 5G手机游戏手机 12GB+256GB 迷雾金', '三星Galaxy Note20 Ultra 5G(SM-N9860)S Pen&三星笔记 120Hz自适应屏幕 5G手机游戏手机 12GB+256GB 迷雾金', '白条12期免息!下单赠价值1199元BudsLive耳机!全场至高优惠4000元!', 9199.00); 57 | INSERT INTO `t_product` VALUES (38, '洁滔(GILLTAO)德国三档强力增压淋浴花洒喷头', '洁滔(GILLTAO)德国三档强力增压淋浴花洒喷头手持沐浴洗澡淋雨大出水可拆洗万向旋转莲蓬头套装 三挡增压止水瓷白款', '12.02号下午2点掌上秒杀,瓷白单喷头仅需19元,两件再享9折,上期秒杀2小时3500单抢光,欲购从速。三挡增压,一键止水,可拆洗,头部万向旋转,不增压包退。', 49.00); 58 | INSERT INTO `t_product` VALUES (39, 'OPPO K7x 新品双模5G手机 90Hz电竞屏 智能拍照游戏 ', 'OPPO K7x 新品双模5G手机 90Hz电竞屏 智能拍照游戏 长续航手机oppok7x 黑镜 6+128G【现货速发】 全网通5G 闪充套装', '【现货速发,到手1499起】购机送蓝牙佴机+1年屏碎险+2年质保+晒单20+会员积分~5G硬核新品K7x赠蓝牙佴机', 1599.00); 59 | INSERT INTO `t_product` VALUES (40, '松下5款吸嘴家用大吸力吸尘器', '松下(panasonic)家用吸尘器大吸力小型地毯床上办公室强力除尘吸尘器宠物家庭适用 MC-WLC87字母负离子吸嘴', '【锁定12.3日10点】限时好价659!错过12.12都没有! 【团购咨询客服】可领取惊喜出厂价哦! 【全国联保,售后无忧】180以换代修!立即加购', 1499.00); 60 | INSERT INTO `t_product` VALUES (41, '阿玛尼商务钢带男士时尚石英表', '【品牌授权】Emporio Armani 阿玛尼手表 欧美表 商务男士腕表 皮带钢带男表石英表 AR1819', '【大牌秒杀】爆款男士钢带表AR819到手价1300更多优惠请咨询再线客服', 2990.00); 61 | INSERT INTO `t_product` VALUES (42, '联想拯救者12GB+256GB 炽焰战甲', '联想拯救者电竞手机Pro 12GB+256GB 骁龙865 Plus 双液冷散热 144Hz电竞屏 双模5G游戏手机 炽焰战甲', '【专享2500元VIP特权】高通骁龙865plus游戏高手的选择,双模5G,144Hz电竞屏,双液冷散热,晒单得50元E卡!至尊透明版', 4199.00); 62 | 63 | SET FOREIGN_KEY_CHECKS = 1; 64 | -------------------------------------------------------------------------------- /sql/timeless_seckill.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | 4 | Source Server : 1 5 | Source Server Type : MySQL 6 | Source Server Version : 80030 7 | Source Host : localhost:3306 8 | Source Schema : timeless_seckill 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 80030 12 | File Encoding : 65001 13 | 14 | Date: 17/06/2023 23:18:03 15 | */ 16 | 17 | DROP DATABASE IF EXISTS `timeless_seckill`; 18 | 19 | CREATE DATABASE `timeless_seckill` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; 20 | 21 | USE `timeless_seckill`; 22 | 23 | SET NAMES utf8mb4; 24 | SET FOREIGN_KEY_CHECKS = 0; 25 | 26 | -- ---------------------------- 27 | -- Table structure for t_order_info 28 | -- ---------------------------- 29 | DROP TABLE IF EXISTS `t_order_info`; 30 | CREATE TABLE `t_order_info` ( 31 | `order_no` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, 32 | `user_id` bigint(0) NULL DEFAULT NULL, 33 | `product_id` bigint(0) NULL DEFAULT NULL, 34 | `seckill_id` bigint(0) NULL DEFAULT NULL, 35 | `product_name` varchar(200) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, 36 | `product_price` decimal(10, 2) NULL DEFAULT NULL, 37 | `seckill_price` decimal(10, 2) NULL DEFAULT NULL, 38 | `status` varchar(10) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT '待付款', 39 | `create_date` datetime(0) NULL DEFAULT NULL, 40 | `pay_date` datetime(0) NULL DEFAULT NULL, 41 | `seckill_date` date NULL DEFAULT NULL, 42 | `pay_price` decimal(10, 2) NULL DEFAULT NULL, 43 | PRIMARY KEY (`order_no`) USING BTREE 44 | ) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC; 45 | 46 | -- ---------------------------- 47 | -- Records of t_order_info 48 | -- ---------------------------- 49 | 50 | -- ---------------------------- 51 | -- Table structure for t_order_to_product 52 | -- ---------------------------- 53 | DROP TABLE IF EXISTS `t_order_to_product`; 54 | CREATE TABLE `t_order_to_product` ( 55 | `id` bigint(0) NOT NULL AUTO_INCREMENT, 56 | `order_no` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '订单编号', 57 | `product_id` bigint(0) NULL DEFAULT NULL COMMENT '商品id', 58 | `product_count` int(0) NULL DEFAULT NULL COMMENT '商品数量', 59 | PRIMARY KEY (`id`) USING BTREE 60 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 61 | 62 | -- ---------------------------- 63 | -- Records of t_order_to_product 64 | -- ---------------------------- 65 | 66 | -- ---------------------------- 67 | -- Table structure for t_seckill_product 68 | -- ---------------------------- 69 | DROP TABLE IF EXISTS `t_seckill_product`; 70 | CREATE TABLE `t_seckill_product` ( 71 | `id` bigint(0) NOT NULL AUTO_INCREMENT, 72 | `product_id` bigint(0) NULL DEFAULT NULL, 73 | `seckill_price` decimal(10, 2) NULL DEFAULT NULL, 74 | `stock_count` int(0) NULL DEFAULT NULL, 75 | PRIMARY KEY (`id`) USING BTREE 76 | ) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC; 77 | 78 | -- ---------------------------- 79 | -- Records of t_seckill_product 80 | -- ---------------------------- 81 | INSERT INTO `t_seckill_product` VALUES (2, 23, 3699.00, 100); 82 | INSERT INTO `t_seckill_product` VALUES (3, 24, 2999.00, 100); 83 | INSERT INTO `t_seckill_product` VALUES (4, 25, 9.00, 100); 84 | INSERT INTO `t_seckill_product` VALUES (5, 26, 899.00, 100); 85 | INSERT INTO `t_seckill_product` VALUES (6, 27, 59.00, 98); 86 | INSERT INTO `t_seckill_product` VALUES (7, 28, 1699.00, 100); 87 | INSERT INTO `t_seckill_product` VALUES (8, 29, 3999.00, 100); 88 | INSERT INTO `t_seckill_product` VALUES (9, 30, 1099.00, 100); 89 | INSERT INTO `t_seckill_product` VALUES (10, 31, 2399.00, 100); 90 | INSERT INTO `t_seckill_product` VALUES (11, 32, 799.00, 100); 91 | INSERT INTO `t_seckill_product` VALUES (12, 33, 199.00, 100); 92 | INSERT INTO `t_seckill_product` VALUES (13, 34, 949.00, 100); 93 | INSERT INTO `t_seckill_product` VALUES (14, 35, 2499.00, 100); 94 | INSERT INTO `t_seckill_product` VALUES (15, 36, 1199.00, 100); 95 | INSERT INTO `t_seckill_product` VALUES (16, 37, 5999.00, 100); 96 | INSERT INTO `t_seckill_product` VALUES (17, 38, 9.00, 100); 97 | INSERT INTO `t_seckill_product` VALUES (18, 39, 999.00, 100); 98 | INSERT INTO `t_seckill_product` VALUES (19, 40, 659.00, 100); 99 | INSERT INTO `t_seckill_product` VALUES (20, 41, 1999.00, 100); 100 | INSERT INTO `t_seckill_product` VALUES (21, 42, 3399.00, 100); 101 | 102 | -- ---------------------------- 103 | -- Table structure for t_shop_cart 104 | -- ---------------------------- 105 | DROP TABLE IF EXISTS `t_shop_cart`; 106 | CREATE TABLE `t_shop_cart` ( 107 | `id` int(0) NOT NULL AUTO_INCREMENT, 108 | `shop_cart_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '购物车id', 109 | `user_id` bigint(0) NULL DEFAULT NULL COMMENT '用户id', 110 | `product_id` bigint(0) NULL DEFAULT NULL COMMENT '商品id', 111 | `product_count` int(0) NULL DEFAULT NULL COMMENT '商品数量', 112 | `product_price` decimal(10, 2) NULL DEFAULT NULL COMMENT '商品价格', 113 | PRIMARY KEY (`id`) USING BTREE 114 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 115 | 116 | -- ---------------------------- 117 | -- Records of t_shop_cart 118 | -- ---------------------------- 119 | 120 | SET FOREIGN_KEY_CHECKS = 1; 121 | -------------------------------------------------------------------------------- /docker/mysql/db/timeless_seckill.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | 4 | Source Server : 1 5 | Source Server Type : MySQL 6 | Source Server Version : 80030 7 | Source Host : localhost:3306 8 | Source Schema : timeless_seckill 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 80030 12 | File Encoding : 65001 13 | 14 | Date: 17/06/2023 23:18:03 15 | */ 16 | 17 | DROP DATABASE IF EXISTS `timeless_seckill`; 18 | 19 | CREATE DATABASE `timeless_seckill` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; 20 | 21 | USE `timeless_seckill`; 22 | 23 | SET NAMES utf8mb4; 24 | SET FOREIGN_KEY_CHECKS = 0; 25 | 26 | -- ---------------------------- 27 | -- Table structure for t_order_info 28 | -- ---------------------------- 29 | DROP TABLE IF EXISTS `t_order_info`; 30 | CREATE TABLE `t_order_info` ( 31 | `order_no` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, 32 | `user_id` bigint(0) NULL DEFAULT NULL, 33 | `product_id` bigint(0) NULL DEFAULT NULL, 34 | `seckill_id` bigint(0) NULL DEFAULT NULL, 35 | `product_name` varchar(200) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, 36 | `product_price` decimal(10, 2) NULL DEFAULT NULL, 37 | `seckill_price` decimal(10, 2) NULL DEFAULT NULL, 38 | `status` varchar(10) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT '待付款', 39 | `create_date` datetime(0) NULL DEFAULT NULL, 40 | `pay_date` datetime(0) NULL DEFAULT NULL, 41 | `seckill_date` date NULL DEFAULT NULL, 42 | `pay_price` decimal(10, 2) NULL DEFAULT NULL, 43 | PRIMARY KEY (`order_no`) USING BTREE 44 | ) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC; 45 | 46 | -- ---------------------------- 47 | -- Records of t_order_info 48 | -- ---------------------------- 49 | 50 | -- ---------------------------- 51 | -- Table structure for t_order_to_product 52 | -- ---------------------------- 53 | DROP TABLE IF EXISTS `t_order_to_product`; 54 | CREATE TABLE `t_order_to_product` ( 55 | `id` bigint(0) NOT NULL AUTO_INCREMENT, 56 | `order_no` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '订单编号', 57 | `product_id` bigint(0) NULL DEFAULT NULL COMMENT '商品id', 58 | `product_count` int(0) NULL DEFAULT NULL COMMENT '商品数量', 59 | PRIMARY KEY (`id`) USING BTREE 60 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 61 | 62 | -- ---------------------------- 63 | -- Records of t_order_to_product 64 | -- ---------------------------- 65 | 66 | -- ---------------------------- 67 | -- Table structure for t_seckill_product 68 | -- ---------------------------- 69 | DROP TABLE IF EXISTS `t_seckill_product`; 70 | CREATE TABLE `t_seckill_product` ( 71 | `id` bigint(0) NOT NULL AUTO_INCREMENT, 72 | `product_id` bigint(0) NULL DEFAULT NULL, 73 | `seckill_price` decimal(10, 2) NULL DEFAULT NULL, 74 | `stock_count` int(0) NULL DEFAULT NULL, 75 | PRIMARY KEY (`id`) USING BTREE 76 | ) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC; 77 | 78 | -- ---------------------------- 79 | -- Records of t_seckill_product 80 | -- ---------------------------- 81 | INSERT INTO `t_seckill_product` VALUES (2, 23, 3699.00, 100); 82 | INSERT INTO `t_seckill_product` VALUES (3, 24, 2999.00, 100); 83 | INSERT INTO `t_seckill_product` VALUES (4, 25, 9.00, 100); 84 | INSERT INTO `t_seckill_product` VALUES (5, 26, 899.00, 100); 85 | INSERT INTO `t_seckill_product` VALUES (6, 27, 59.00, 98); 86 | INSERT INTO `t_seckill_product` VALUES (7, 28, 1699.00, 100); 87 | INSERT INTO `t_seckill_product` VALUES (8, 29, 3999.00, 100); 88 | INSERT INTO `t_seckill_product` VALUES (9, 30, 1099.00, 100); 89 | INSERT INTO `t_seckill_product` VALUES (10, 31, 2399.00, 100); 90 | INSERT INTO `t_seckill_product` VALUES (11, 32, 799.00, 100); 91 | INSERT INTO `t_seckill_product` VALUES (12, 33, 199.00, 100); 92 | INSERT INTO `t_seckill_product` VALUES (13, 34, 949.00, 100); 93 | INSERT INTO `t_seckill_product` VALUES (14, 35, 2499.00, 100); 94 | INSERT INTO `t_seckill_product` VALUES (15, 36, 1199.00, 100); 95 | INSERT INTO `t_seckill_product` VALUES (16, 37, 5999.00, 100); 96 | INSERT INTO `t_seckill_product` VALUES (17, 38, 9.00, 100); 97 | INSERT INTO `t_seckill_product` VALUES (18, 39, 999.00, 100); 98 | INSERT INTO `t_seckill_product` VALUES (19, 40, 659.00, 100); 99 | INSERT INTO `t_seckill_product` VALUES (20, 41, 1999.00, 100); 100 | INSERT INTO `t_seckill_product` VALUES (21, 42, 3399.00, 100); 101 | 102 | -- ---------------------------- 103 | -- Table structure for t_shop_cart 104 | -- ---------------------------- 105 | DROP TABLE IF EXISTS `t_shop_cart`; 106 | CREATE TABLE `t_shop_cart` ( 107 | `id` int(0) NOT NULL AUTO_INCREMENT, 108 | `shop_cart_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '购物车id', 109 | `user_id` bigint(0) NULL DEFAULT NULL COMMENT '用户id', 110 | `product_id` bigint(0) NULL DEFAULT NULL COMMENT '商品id', 111 | `product_count` int(0) NULL DEFAULT NULL COMMENT '商品数量', 112 | `product_price` decimal(10, 2) NULL DEFAULT NULL COMMENT '商品价格', 113 | PRIMARY KEY (`id`) USING BTREE 114 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 115 | 116 | -- ---------------------------- 117 | -- Records of t_shop_cart 118 | -- ---------------------------- 119 | 120 | SET FOREIGN_KEY_CHECKS = 1; 121 | -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/java/com/timeless/config/RabbitMQConfig.java: -------------------------------------------------------------------------------- 1 | package com.timeless.config; 2 | 3 | import org.springframework.amqp.core.*; 4 | import org.springframework.amqp.rabbit.connection.ConnectionFactory; 5 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 6 | import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; 7 | import org.springframework.beans.factory.annotation.Qualifier; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | import java.util.Objects; 14 | 15 | /** 16 | * @Description: RabbitMQConfig类中的@Bean注解用于将队列、交换机、绑定关系等RabbitMQ相关的组件注册为Spring的bean, 17 | * 从而实现这些组件的自动化配置和管理。只有将这些组件注册为bean,才能够在应用程序中使用它们, 18 | * 并且能够在RabbitMQ中创建对应的队列、交换机等资源。如果没有将这些组件注册为bean, 19 | * 那么在应用程序中是无法使用它们的,也不可能在RabbitMQ中创建对应的队列、交换机等资源。 20 | * 因此,将队列、交换机等组件注册为bean是使用RabbitMQ的必要步骤之一。 21 | * @Date: 2023/5/29 19:54 22 | * @Author: timeless 23 | */ 24 | @Configuration 25 | public class RabbitMQConfig { 26 | 27 | public static final String REFUND_QUEUE = "refund_queue"; 28 | public static final String REFUND_ERROR_QUEUE = "refund_error_queue"; 29 | 30 | 31 | /** 32 | * ttl交换机 33 | */ 34 | public static final String TTL_EXCHANGE = "springboot_ttl_exchange"; 35 | /** 36 | * ttl队列 37 | */ 38 | public static final String TTL_QUEUE = "springboot_ttl_queue"; 39 | /** 40 | * 死信交换机 41 | */ 42 | public static final String DLX_EXCHANGE = "springboot_dlx_exchange"; 43 | /** 44 | * 死信队列 45 | */ 46 | public static final String DLX_QUEUE = "springboot_dlx_queue"; 47 | 48 | 49 | // 基于插件的交换机和队列 50 | public static final String PLUGINS_QUEUE = "plugins_queue"; 51 | 52 | public static final String PLUGINS_EXCHANGE = "plugins_exchange"; 53 | 54 | @Bean 55 | public CustomExchange pluginsExchange() { 56 | HashMap arguments = new HashMap<>(); 57 | arguments.put("x-delayed-type", ExchangeTypes.TOPIC); 58 | return new CustomExchange(PLUGINS_EXCHANGE, "x-delayed-message", true, false, arguments); 59 | } 60 | 61 | @Bean 62 | public Queue pluginsQueue() { 63 | return QueueBuilder.durable(PLUGINS_QUEUE).build(); 64 | } 65 | 66 | @Bean 67 | public Binding bindingPlugins(@Qualifier("pluginsQueue") Queue queue, @Qualifier("pluginsExchange") CustomExchange exchange) { 68 | return BindingBuilder.bind(queue).to(exchange).with("plugins.#").noargs(); 69 | } 70 | 71 | 72 | /** 73 | * 声明ttl交换机 74 | * 75 | * @return ttl交换机 76 | */ 77 | @Bean 78 | public Exchange ttlExchange() { 79 | return ExchangeBuilder.topicExchange(TTL_EXCHANGE).build(); 80 | } 81 | 82 | /** 83 | * 声明ttl队列,同时绑定死信交换机 84 | *

85 | *

86 | * 这里使用了RabbitMQ的TTL(Time To Live)和DLX(Dead Letter Exchange)功能来实现延时队列和自动取消订单的功能。 87 | * 当你发送一个订单信息到ttl队列时,队列会设置一个过期时间,这里是60秒(即1分钟), 88 | * 当消息在队列中存活时间超过60秒后,RabbitMQ会将该消息发送到绑定的DLX交换机上, 89 | * 并且将消息路由到绑定的DLX队列上,这里是springboot_dlx_queue。 90 | *

91 | * 当订单信息在ttl队列中存活时间超过60秒后,会被发送到springboot_dlx_queue队列上, 92 | * 这时@RabbitListener(queues = "springboot_dlx_queue")注解的listener方法就会被触发,自动取消订单。 93 | *

94 | * 需要注意的是,ttl队列和DLX队列的绑定关系是在ttl队列的声明中设置的,即在ttlQueue()方法中设置: 95 | *

96 | * 这里设置了ttl队列的死信交换机为DLX_EXCHANGE,死信路由键为"dlx.haha", 97 | * 所以当订单信息在ttl队列中过期后,会被发送到DLX_EXCHANGE交换机并且路由键为"dlx.haha"的队列上,即springboot_dlx_queue队列。 98 | *

99 | * 信息在ttl队列里面并不会被消费,只是等待超时后被自动转发到死信交换机,最终到达死信队列。 100 | * 这是因为ttl队列的消息会在一定时间后自动过期,但是如果没有设置死信交换机,那么过期的消息就会被直接丢弃,无法被消费。 101 | * 因此,通过设置死信交换机,可以将过期的消息转发到指定的死信队列中,以便后续进行处理。 102 | * 103 | * @return ttl队列 104 | */ 105 | 106 | /** 107 | * @Description: 这边还有一个细节,ttl是设置消息过期时间、expires是设置队列过期时间 108 | * @Date: 2023/6/17 21:42 109 | * @Author: timeless 110 | */ 111 | @Bean 112 | public Queue ttlQueue() { 113 | return QueueBuilder.durable(TTL_QUEUE) 114 | //该队列中的消息过期时间,单位毫秒 115 | // .ttl(60000) 116 | //绑定死信交换机 117 | .deadLetterExchange(DLX_EXCHANGE) 118 | //设置死信交换机routingKey 119 | .deadLetterRoutingKey("dlx.haha") 120 | //设置队列最大消息数量为100 121 | .maxLength(100) 122 | .build(); 123 | } 124 | 125 | /** 126 | * 声明死信交换机 127 | * 128 | * @return 死信交换机 129 | */ 130 | @Bean 131 | public Exchange dlxExchange() { 132 | return ExchangeBuilder.topicExchange(DLX_EXCHANGE).build(); 133 | } 134 | 135 | /** 136 | * 声明死信队列 137 | * 138 | * @return 死信队列 139 | */ 140 | @Bean 141 | public Queue dlxQueue() { 142 | return QueueBuilder.durable(DLX_QUEUE).build(); 143 | } 144 | 145 | /** 146 | * 绑定ttl队列到ttl交换机 147 | * 148 | * @param queue ttl队列 149 | * @param exchange ttl交换机 150 | */ 151 | @Bean 152 | public Binding bindingTtl(@Qualifier("ttlQueue") Queue queue, 153 | @Qualifier("ttlExchange") Exchange exchange) { 154 | return BindingBuilder.bind(queue).to(exchange).with("ttl.#").noargs(); 155 | } 156 | 157 | /** 158 | * 绑定死信队列到死信交换机 159 | * 160 | * @param queue 死信队列 161 | * @param exchange 死信交换机 162 | */ 163 | @Bean 164 | public Binding bindingDlx(@Qualifier("dlxQueue") Queue queue, 165 | @Qualifier("dlxExchange") Exchange exchange) { 166 | return BindingBuilder.bind(queue).to(exchange).with("dlx.#").noargs(); 167 | } 168 | 169 | 170 | @Bean 171 | public Queue refundQueue() { 172 | return new Queue(REFUND_QUEUE); 173 | } 174 | 175 | @Bean 176 | public Queue refundErrorQueue() { 177 | return new Queue(REFUND_ERROR_QUEUE); 178 | } 179 | 180 | 181 | /** 182 | * @Description: 以下两个Bean,创建Jackson2JsonMessageConverter对象,使得监听端可以接受任意类型的消息(主要是自定义的类型) 183 | * @Date: 2023/5/29 19:57 184 | * @Author: timeless 185 | */ 186 | @Bean 187 | public Jackson2JsonMessageConverter jackson2JsonMessageConverter() { 188 | return new Jackson2JsonMessageConverter(); 189 | } 190 | 191 | @Bean 192 | public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) { 193 | RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); 194 | rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter()); 195 | return rabbitTemplate; 196 | } 197 | } -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/controller/OrderPayController.java: -------------------------------------------------------------------------------- 1 | package com.timeless.controller; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; 4 | import com.timeless.constants.AppHttpCodeEnum; 5 | import com.timeless.domain.OrderInfo; 6 | import com.timeless.exception.SystemException; 7 | import com.timeless.feign.PayFeign; 8 | import com.timeless.result.ResponseResult; 9 | import com.timeless.service.OrderInfoService; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | import java.util.Date; 14 | import java.util.Map; 15 | import java.util.Objects; 16 | 17 | /** 18 | * @author timeless 19 | * @date 2023/5/28 13:39 20 | * @desciption: 21 | */ 22 | @RestController 23 | @RequestMapping("/orderPay") 24 | public class OrderPayController { 25 | 26 | @Autowired 27 | private OrderInfoService orderInfoService; 28 | 29 | @Autowired 30 | private PayFeign payFeign; 31 | 32 | /** 33 | * 支付 34 | * 35 | * @param orderNo 36 | * @return 这个支付宝返回的是一串html代码,String类型的,显示在界面上 37 | * 也就是说,这个方法完结之后,会让我们填写支付宝的账号密码(或者扫码) 38 | * 那么,在这段时间内,或许会有别人动这个订单,改变这个订单的状态(比如两个人抢着支付) 39 | * 但是,好像也不慌,因为同一订单,在支付宝那边有幂等性操作,不会支付两次。 40 | * 当下单那一刻,如果订单已经支付过,他会提醒的,回调就不走了。 41 | * 哎,那这样岂不是不用考虑回调函数中的订单状态了? 42 | * 不是的,订单已支付自然不用考虑了,返回成功就可以。 43 | * 但是订单超时取消,要是在付款的时候出现,我们这边现在暂时目前还没有让支付宝能知道这个订单已取消, 44 | * 所以会出现:已付款,但是提示用户未成功付款而且订单状态也依然是”已取消“。 45 | * 对应策略: 见下异步回调 46 | */ 47 | @GetMapping("/pay") 48 | public Object pay(String orderNo) { 49 | ResponseResult result = orderInfoService.pay(orderNo); 50 | return result.getData(); 51 | } 52 | 53 | /** 54 | * 退款 55 | * 讲道理,如果按现在的做法,先退款,再修改订单可能会有点问题,因为订单状态可能会延迟改变,这期间用户看到的订单还是已支付。 56 | * 但是,用户量小的情况下,接口很快,也没有太大问题,肯定可以优化! 57 | *

58 | *

59 | * 2023/5/29 23:18 测试订单在“待付款”和“已取消”状态为什么调退款的接口会退不了款。 60 | * 原因:退款接口调用的是支付宝那边的接口,那边会那订单号去看看他那边有没有这个订单的交易成功记录, 61 | * 如果有,那么就可以退款。否则,退款失败。事实上,支付宝只知道我们的订单是成功了还是没成功, 62 | * 具体的超时、取消他并不知道,至少在我现在的代码中,他不知道,或许有回调可以通知他取消订单。 63 | * 所以,我在用“待付款”和“已取消”的订单做测试的会不成功,因为支付宝那边根本没有这条订单的付款记录,不可能退款的。 64 | * 支付宝会但会给我们“订单不存在”的信息。 65 | * 66 | * @param orderNo 67 | * @return 68 | */ 69 | @RequestMapping("/refund/{orderNo}") 70 | public ResponseResult refund(@PathVariable("orderNo") String orderNo) { 71 | OrderInfo orderInfo = orderInfoService.getById(orderNo); 72 | orderInfoService.refund(orderInfo); 73 | return ResponseResult.okResult("退款成功!"); 74 | } 75 | 76 | /** 77 | * 异步回调 78 | * 79 | * @param params 80 | * @return 81 | */ 82 | @RequestMapping("/notifyUrl") 83 | public String notifyUrl(@RequestParam Map params) { 84 | System.out.println("异步回调......"); 85 | 86 | // 判断订单状态 , 如果不是待付款,就返回失败 87 | // 我们需要在if(!AppHttpCodeEnum.CONTINUE_PAY.equals(orderInfo.getStatus())){ 88 | // return "fail"; 89 | // },这个代码里,再做一些保证操作。 90 | // 因为,当这个异步回调被调用的时候,实际上已经付款成功了,但是订单状态这时候却不是“待付款”, 91 | // 意味着,他是已取消或者已付款。但是不管怎么说,钱都付了,不能只返回fail, 92 | // 还要做一些操作,比如通知客服看一下为什么要付款的订单却出现了“已取消”或者“已支付”。 93 | 94 | // 哇,我做了一个实验得到了一个重要的结论: 实验:同时打开两个支付界面。先支付A,A支付完成,订单状态也改变成”已支付“。 95 | // 这时候,再支付B,B会提示订单已支付,这是支付宝那边做的幂等性操作。 96 | // 结论:这边不需要判断订单”已支付“状态了,支付宝有幂等性操作。 97 | // 但是订单”已取消“还是需要考虑的,因为,虽然是”已取消“状态,但实际上还是付了款的, 98 | // 所以需要联系客服退款! 99 | 100 | OrderInfo orderInfo = orderInfoService.getById(params.get("out_trade_no")); 101 | if (!AppHttpCodeEnum.CONTINUE_PAY.getMsg().equals(orderInfo.getStatus())) { 102 | // return "fail"; 103 | // 能走到这里,说明订单状态是:”已取消“,但是用户已经付钱了。所以应对策略是:rnm,退钱!(调用退款接口) 104 | // 也就是用户刚好付完款了,代码走到这里回调了,却超时了,那肯定会在这提示支付失败,然后退钱。(用户的锅,谁让他卡点支付,刚好超时了。) 105 | // 如果怕退款接口超时,可以用MQ异步退款 106 | // 告诉用户订单已经超时,钱一会儿退给您。 107 | refund(params.get("out_trade_no")); 108 | throw new SystemException(AppHttpCodeEnum.PAY_FAIL); 109 | } 110 | 111 | // 手动模拟签名被修改 112 | // 结果:确实抛出了订单失败的异常,但是钱已经在支付宝那边扣款了 113 | // 结论:在这个回调之前,在点击支付之后,就已经扣款了,进入到这个回调函数一定是扣了款了。 114 | // params.put("out_trade_no" , "1111111"); 115 | 116 | 117 | //验证签名 118 | ResponseResult res = payFeign.rsaCheckV1(params); 119 | if (Objects.isNull(res) || !res.getData()) { 120 | throw new SystemException(AppHttpCodeEnum.RSACHECK_FAIL); 121 | } 122 | // 验证签名成功,修改订单状态为“已付款” 123 | // 这里面还需要在sql语句写,订单状态只能从“待付款”到“已付款”(已加上),不然就不成功,下面处理 124 | // 但是走到这里出错的可能性不高,因为前面判断过状态了,走到这里的话,99.99%的可能是“待付款” 125 | // 但是为了以防万一,刚好在上面判断成功,到这里的期间订单过期了。但是用户已付款,就需要退钱了。 126 | boolean update = orderInfoService.update(new UpdateWrapper() 127 | .set("status", AppHttpCodeEnum.DONE_PAY.getMsg()) 128 | .set("pay_date", new Date()) 129 | .eq("order_no", params.get("out_trade_no")) 130 | .eq("status", AppHttpCodeEnum.CONTINUE_PAY.getMsg())); 131 | if (!update) { 132 | // 联系客服 ,退款操作,因为用户已支付 133 | // 或者调用refund接口 134 | // 或者把更改订单状态的消息放在mq中,异步处理。 135 | // (或者可以说把修改订单状态一起放入RabbitMQ中,见OrderInfoServiceImpl.refund()。后面有案例,这里就不做演示。) 136 | orderInfoService.refund(orderInfo); 137 | } 138 | return "success"; 139 | } 140 | 141 | @RequestMapping("/returnUrl") 142 | public ResponseResult returnUrl(@RequestParam Map params) { 143 | System.out.println("同步回调......"); 144 | 145 | // 判断订单状态 , 如果不是待付款,就返回失败 146 | // 这边不需要了,因为回调跟付款就几毫秒的差距,虽然可能失败,行!考虑一下失败的后果和对策! 147 | // 抛异常,这个到这边的情况是:1. 钱已经付了 2. 订单状态肯定不是待付款 148 | // 至于对策:同上,比如通知客服看一下为什么要付款的订单却出现了“已取消”或者“已支付”。 149 | 150 | // 这边其实不需要判断了,因为在我测试的过程中,异步一定比同步快(其他情况不一定) 151 | // 这样的话,异步那边修改了订单状态,这边一定会抛异常,所以不需要! 152 | // 除了签名之外的判断,都在异步回调那边做(但是好像也没啥判断的了) 153 | 154 | // OrderInfo orderInfo = orderInfoService.getById(params.get("out_trade_no")); 155 | // if(!AppHttpCodeEnum.CONTINUE_PAY.getMsg().equals(orderInfo.getStatus())){ 156 | //// return ResponseResult.errorResult(AppHttpCodeEnum.PAY_FAIL); 157 | // throw new SystemException(AppHttpCodeEnum.PAY_FAIL); 158 | // } 159 | 160 | ResponseResult res = payFeign.rsaCheckV1(params); 161 | if (Objects.isNull(res) || !res.getData()) { 162 | throw new SystemException(AppHttpCodeEnum.RSACHECK_FAIL); 163 | } 164 | //返回订单信息 165 | return ResponseResult.okResult(orderInfoService.getById(params.get("out_trade_no"))); 166 | } 167 | 168 | 169 | } 170 | -------------------------------------------------------------------------------- /timeless-order-pay-api/src/main/java/com/timeless/utils/SnowFlakeUtil.java: -------------------------------------------------------------------------------- 1 | package com.timeless.utils; 2 | 3 | import cn.hutool.core.convert.Convert; 4 | 5 | import java.util.concurrent.TimeUnit; 6 | import java.util.concurrent.locks.LockSupport; 7 | 8 | import static org.bouncycastle.asn1.x500.style.RFC4519Style.cn; 9 | 10 | /** 11 | * @author zdj 12 | * @version 1.0 13 | * @date 2022/05/30 15:35 14 | */ 15 | public class SnowFlakeUtil { 16 | 17 | // ==============================Fields=========================================== 18 | /** 19 | * 开始时间截 (2021-01-19) 20 | */ 21 | private final long twepoch = 1611043703161L; 22 | 23 | /** 24 | * 机器id所占的位数 25 | */ 26 | private final long workerIdBits = 5L; 27 | 28 | /** 29 | * 数据标识id所占的位数 30 | */ 31 | private final long dataCenterIdBits = 5L; 32 | 33 | /** 34 | * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) 35 | */ 36 | private final long maxWorkerId = -1L ^ (-1L << workerIdBits); 37 | 38 | /** 39 | * 支持的最大数据标识id,结果是31 40 | */ 41 | private final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits); 42 | 43 | /** 44 | * 序列在id中占的位数 45 | */ 46 | private final long sequenceBits = 12L; 47 | 48 | /** 49 | * 机器ID向左移12位 50 | */ 51 | private final long workerIdShift = sequenceBits; 52 | 53 | /** 54 | * 数据标识id向左移17位(12+5) 55 | */ 56 | private final long dataCenterIdShift = sequenceBits + workerIdBits; 57 | 58 | /** 59 | * 时间截向左移22位(5+5+12) 60 | */ 61 | private final long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits; 62 | 63 | /** 64 | * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) 65 | */ 66 | private final long sequenceMask = -1L ^ (-1L << sequenceBits); 67 | 68 | /** 69 | * 工作机器ID(0~31) 70 | */ 71 | private long workerId; 72 | 73 | /** 74 | * 数据中心ID(0~31) 75 | */ 76 | private long dataCenterId; 77 | 78 | /** 79 | * 备用的数据中心ID(0~31),当时钟回拨时,为了不抛异常,启用备用ID 80 | */ 81 | private long standbyDatacenterId; 82 | 83 | /** 84 | * 毫秒内序列(0~4095) 85 | */ 86 | private long sequence = 0L; 87 | 88 | /** 89 | * 上次生成ID的时间截 90 | */ 91 | private long lastTimestamp = -1L; 92 | 93 | /** 94 | * 是否时钟回拨 95 | */ 96 | private boolean isTimestampBack = false; 97 | 98 | 99 | // ==============================Constructors===================================== 100 | /** 101 | * 最大容忍时间, 单位毫秒, 即如果时钟只是回拨了该变量指定的时间, 那么等待相应的时间即可; 102 | * 考虑到sequence服务的高性能, 这个值不易过大 103 | */ 104 | private static final long MAX_BACKWARD_MS = 3; 105 | 106 | /** 107 | * 构造函数 108 | * @param workerId 工作ID (0~31) 109 | * @param datacenterId 数据中心ID (0~31) 110 | */ 111 | SnowFlakeUtil(long workerId, long datacenterId, long standbyDatacenterId) { 112 | if (workerId > maxWorkerId || workerId < 0) { 113 | throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); 114 | } 115 | if (datacenterId > maxDataCenterId || datacenterId < 0) { 116 | throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDataCenterId)); 117 | } 118 | if (standbyDatacenterId > maxDataCenterId || standbyDatacenterId < 0) { 119 | throw new IllegalArgumentException(String.format("standby datacenter Id can't be greater than %d or less than 0", maxDataCenterId)); 120 | } 121 | if (datacenterId == standbyDatacenterId) { 122 | throw new IllegalArgumentException("datacenter Id can't equal to standby datacenter Id."); 123 | } 124 | this.workerId = workerId; 125 | this.dataCenterId = datacenterId; 126 | this.standbyDatacenterId = standbyDatacenterId; 127 | } 128 | 129 | 130 | // ==============================Methods========================================== 131 | /** 132 | * 获得下一个ID (该方法是线程安全的) 133 | * @return SnowflakeId 134 | */ 135 | synchronized long nextId() { 136 | long timestamp = timeGen(); 137 | // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过 138 | if (timestamp < lastTimestamp) { 139 | // 如果时钟回拨在可接受范围内, 等待即可 140 | long offset = lastTimestamp - timestamp; 141 | if (offset <= MAX_BACKWARD_MS) { 142 | try { 143 | //睡(lastTimestamp - currentTimestamp)ms让其追上 144 | LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(offset)); 145 | 146 | timestamp = timeGen(); 147 | //如果当前时间还是小于上一次ID生成的时间戳,这时启用备用的datacenterId 148 | if (timestamp < lastTimestamp) { 149 | isTimestampBack = true; 150 | //服务器时钟被调整了 151 | //log.error(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); 152 | //throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); 153 | } else { 154 | isTimestampBack = false; 155 | } 156 | } catch (Exception e) { 157 | //log.error(e); 158 | } 159 | } 160 | } 161 | 162 | // 如果是同一时间生成的,则进行毫秒内序列 163 | if (lastTimestamp == timestamp) { 164 | sequence = (sequence + 1) & sequenceMask; 165 | // 毫秒内序列溢出 166 | if (sequence == 0) { 167 | // 阻塞到下一个毫秒,获得新的时间戳 168 | timestamp = tilNextMillis(lastTimestamp); 169 | } 170 | } 171 | // 时间戳改变,毫秒内序列重置 172 | else { 173 | sequence = 0L; 174 | } 175 | 176 | // 上次生成ID的时间截 177 | lastTimestamp = timestamp; 178 | 179 | //要使用的datacenterId 180 | long datacenterIdToUse = isTimestampBack ? standbyDatacenterId : dataCenterId; 181 | 182 | // 移位并通过或运算拼到一起组成64位的ID 183 | return ((timestamp - twepoch) << timestampLeftShift) // 184 | | (datacenterIdToUse << dataCenterIdShift) // 185 | | (workerId << workerIdShift) // 186 | | sequence; 187 | } 188 | 189 | /** 190 | * 阻塞到下一个毫秒,直到获得新的时间戳 191 | * @param lastTimestamp 上次生成ID的时间截 192 | * @return 当前时间戳 193 | */ 194 | private long tilNextMillis(long lastTimestamp) { 195 | long timestamp = timeGen(); 196 | while (timestamp <= lastTimestamp) { 197 | timestamp = timeGen(); 198 | } 199 | return timestamp; 200 | } 201 | 202 | /** 203 | * 返回以毫秒为单位的当前时间 204 | * @return 当前时间(毫秒) 205 | */ 206 | protected long timeGen() { 207 | return System.currentTimeMillis(); 208 | } 209 | 210 | /** 211 | * 使用双重校验获取实例对象 212 | */ 213 | private volatile static SnowFlakeUtil snowflakeIdWorker; 214 | public static SnowFlakeUtil getInstance() { 215 | if (snowflakeIdWorker == null) { 216 | synchronized (SnowFlakeUtil.class) { 217 | if (snowflakeIdWorker == null) { 218 | snowflakeIdWorker = new SnowFlakeUtil(1, 0,11); 219 | } 220 | } 221 | } 222 | return snowflakeIdWorker; 223 | } 224 | 225 | /** 226 | * 获得下一个ID (该方法是线程安全的) 227 | * @return SnowflakeId 228 | */ 229 | public static String getNextId() { 230 | return Convert.toStr(getInstance().nextId()); 231 | } 232 | } 233 | 234 | -------------------------------------------------------------------------------- /timeless-seckill-server/src/main/java/com/timeless/service/impl/OrderInfoServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.timeless.service.impl; 2 | 3 | import cn.hutool.core.util.BooleanUtil; 4 | import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; 5 | import com.baomidou.mybatisplus.core.toolkit.StringUtils; 6 | import com.baomidou.mybatisplus.core.toolkit.Wrappers; 7 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 8 | import com.timeless.config.RabbitMQConfig; 9 | import com.timeless.constants.AppHttpCodeEnum; 10 | import com.timeless.domain.OrderInfo; 11 | import com.timeless.domain.OrderToProduct; 12 | import com.timeless.domain.SeckillProduct; 13 | import com.timeless.domain.ShopCart; 14 | import com.timeless.domain.vo.PayVo; 15 | import com.timeless.domain.vo.RefundVo; 16 | import com.timeless.domain.vo.SeckillProductVo; 17 | import com.timeless.exception.SystemException; 18 | import com.timeless.feign.PayFeign; 19 | import com.timeless.mapper.OrderInfoMapper; 20 | import com.timeless.result.ResponseResult; 21 | import com.timeless.service.OrderInfoService; 22 | import com.timeless.service.OrderToProductService; 23 | import com.timeless.service.SeckillProductService; 24 | import com.timeless.service.ShopCartService; 25 | import com.timeless.utils.DateTimeUtils; 26 | import com.timeless.utils.IdGenerateUtil; 27 | import lombok.extern.slf4j.Slf4j; 28 | import org.jetbrains.annotations.NotNull; 29 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 30 | import org.springframework.aop.framework.AopContext; 31 | import org.springframework.beans.BeanUtils; 32 | import org.springframework.beans.factory.annotation.Autowired; 33 | import org.springframework.beans.factory.annotation.Value; 34 | import org.springframework.data.redis.core.StringRedisTemplate; 35 | import org.springframework.stereotype.Service; 36 | import org.springframework.transaction.annotation.Transactional; 37 | 38 | import java.util.ArrayList; 39 | import java.util.Date; 40 | import java.util.List; 41 | import java.util.Objects; 42 | import java.util.stream.Collectors; 43 | 44 | /** 45 | * (OrderInfo)表服务实现类 46 | * 47 | * @author makejava 48 | * @since 2023-05-28 11:51:49 49 | */ 50 | @Service("orderInfoService") 51 | @Slf4j 52 | public class OrderInfoServiceImpl extends ServiceImpl implements OrderInfoService { 53 | 54 | @Autowired 55 | private SeckillProductService seckillProductService; 56 | 57 | @Autowired 58 | private OrderInfoService orderInfoService; 59 | 60 | @Autowired 61 | private PayFeign payFeign; 62 | 63 | @Autowired 64 | private RabbitTemplate rabbitTemplate; 65 | 66 | @Autowired 67 | private ShopCartService shopCartService; 68 | 69 | @Autowired 70 | private OrderToProductService orderToProductService; 71 | 72 | @Autowired 73 | private StringRedisTemplate stringRedisTemplate; 74 | 75 | @Value("${alipay.returnUrl}") 76 | private String returnUrl; 77 | 78 | 79 | @Value("${alipay.notifyUrl}") 80 | private String notifyUrl; 81 | 82 | @Override 83 | public OrderInfo doSeckill(Long userId, Long seckillId, String expireTime) { 84 | 85 | 86 | // 0. 得到商品信息 87 | SeckillProductVo seckillProductVo = seckillProductService.getSeckkillProductVo(seckillId); 88 | 89 | /** 90 | * @Description: 在这个方法中,如果没有查询到商品,建议抛出异常,而不是返回null。原因如下: 91 | * 92 | * 1. 异常具有更好的可读性和可维护性。如果返回null,那么调用方需要判断返回值是否为null,这样会增加代码的复杂度和出错的可能性。 93 | * 而如果抛出异常,那么调用方可以直接捕获异常并进行相应的处理,代码更加简洁明了,也更容易定位问题。 94 | * 95 | * 2. 异常可以提供更好的错误信息。如果返回null,那么调用方无法得知具体的错误原因,需要通过日志等方式来查看。 96 | * 而如果抛出异常,那么可以在异常信息中提供具体的错误原因和错误码,方便调用方进行处理和排查问题。 97 | * 98 | * 3. 异常可以避免空指针异常等问题。如果返回null,那么调用方在使用返回值时需要进行判空操作,否则可能会出现空指针异常等问题。 99 | * 而如果抛出异常,那么可以避免这种问题的发生。 100 | * 101 | * 因此,建议在这个方法中,如果没有查询到商品,抛出异常并提供具体的错误码和错误信息,以提高代码的可读性和可维护性。 102 | * @Date: 2023/5/29 20:14 103 | * @Author: timeless 104 | */ 105 | if (Objects.isNull(seckillProductVo)) { 106 | throw new SystemException(AppHttpCodeEnum.SECKILL_PRODUCT_NOT_EXIST); 107 | } 108 | 109 | // 2. 保证库存数量足够 110 | // 之后优化,用缓存redis之类的,来应对高并发的场景。 111 | if (seckillProductVo.getStockCount() <= 0) { 112 | throw new SystemException(AppHttpCodeEnum.STOCK_NOT_ENOUGH); 113 | } 114 | 115 | // 3. 创建秒杀订单 , 扣减库存 116 | // redis分布式锁保证一人一单 117 | ILockImpl iLock = new ILockImpl(stringRedisTemplate, "order:" + AppHttpCodeEnum.USERID); 118 | boolean b = iLock.tryLock(100); 119 | if (!b) { 120 | //获取锁失败,意味着同一个分布式系统下,同一个人已经获取了锁,也就是正在下单中,此时需要防止再次尝试下单 121 | throw new SystemException(AppHttpCodeEnum.HAS_BUY); 122 | } 123 | OrderInfo orderInfo; 124 | try { 125 | // 事务要交给代理对象调用,因为这是spring管理的 126 | OrderInfoService proxy = (OrderInfoService) AopContext.currentProxy(); 127 | orderInfo = proxy.saveOrder(userId, seckillId, seckillProductVo); 128 | }finally { 129 | iLock.unlock(); 130 | } 131 | 132 | 133 | log.error("下单成功....." + DateTimeUtils.getCurrentDateTime() + " ===== 订单号:" + orderInfo.getOrderNo()); 134 | 135 | //实现订单超时自定义时间,自动取消,RabbitMQ实现 136 | rabbitTemplate.convertAndSend(RabbitMQConfig.TTL_EXCHANGE, "ttl.test", orderInfo, msg -> { 137 | msg.getMessageProperties().setExpiration(expireTime); 138 | return msg; 139 | }); 140 | return orderInfo; 141 | } 142 | 143 | @Transactional 144 | @Override 145 | public OrderInfo saveOrder(Long userId, Long seckillId, SeckillProductVo seckillProductVo) { 146 | OrderInfo orderInfo; 147 | 148 | long count = count(Wrappers.lambdaQuery() 149 | .eq(OrderInfo::getUserId, AppHttpCodeEnum.USERID) 150 | .eq(OrderInfo::getSeckillId, seckillId)); 151 | 152 | if (count > 0) { 153 | throw new SystemException(AppHttpCodeEnum.HAS_BUY); 154 | } 155 | 156 | 157 | // 扣减库存 158 | UpdateWrapper updateWrapper = new UpdateWrapper<>(); 159 | updateWrapper.setSql("stock_count = stock_count - 1") 160 | .gt("stock_count", 0) 161 | .eq("id", seckillProductVo.getId()); 162 | boolean update = seckillProductService.update(updateWrapper); 163 | 164 | if (BooleanUtil.isFalse(update)) { 165 | throw new SystemException(AppHttpCodeEnum.STOCK_NOT_ENOUGH); 166 | } 167 | 168 | //生成订单 169 | orderInfo = new OrderInfo(); 170 | BeanUtils.copyProperties(seckillProductVo, orderInfo); 171 | orderInfo.setUserId(userId); 172 | orderInfo.setCreateDate(new Date()); 173 | orderInfo.setOrderNo(String.valueOf(IdGenerateUtil.get().nextId())); 174 | orderInfo.setSeckillId(seckillProductVo.getId()); 175 | orderInfo.setProductPrice(seckillProductVo.getProductPrice()); 176 | save(orderInfo); 177 | return orderInfo; 178 | } 179 | 180 | @Override 181 | public ResponseResult pay(String orderNo) { 182 | //根据订单号拿到订单对象 183 | OrderInfo orderInfo = orderInfoService.getById(orderNo); 184 | PayVo payVo = new PayVo(); 185 | payVo.setOutTradeNo(orderNo); 186 | if (StringUtils.isBlank(orderInfo.getProductName())) { 187 | payVo.setTotalAmount(String.valueOf(orderInfo.getPayPrice())); 188 | payVo.setSubject("批量下单"); 189 | payVo.setBody("批量下单"); 190 | } else { 191 | payVo.setTotalAmount(String.valueOf(orderInfo.getSeckillPrice())); 192 | payVo.setSubject(orderInfo.getProductName()); 193 | payVo.setBody(orderInfo.getProductName()); 194 | } 195 | payVo.setReturnUrl(returnUrl); 196 | payVo.setNotifyUrl(notifyUrl); 197 | 198 | ResponseResult result = payFeign.payOnline(payVo); 199 | return result; 200 | } 201 | 202 | /** 203 | * 哇塞,完美出现,钱扣了,订单状态没改变,因为.set("status", AppHttpCodeEnum.DONE_REFUND.getMsg())忘了.getMsg(); 204 | * 205 | * @param orderInfo 206 | */ 207 | @Override 208 | public void refund(OrderInfo orderInfo) { 209 | RefundVo refundVo = new RefundVo(); 210 | refundVo.setOutTradeNo(orderInfo.getOrderNo()); 211 | refundVo.setRefundAmount(String.valueOf(orderInfo.getSeckillPrice())); 212 | refundVo.setRefundReason("没钱了...."); 213 | 214 | //RabbitMQ中放入退款消息 215 | rabbitTemplate.convertAndSend(RabbitMQConfig.REFUND_QUEUE, refundVo); 216 | 217 | 218 | // ResponseResult result = payFeign.refund(refundVo); 219 | // if (Objects.isNull(result) || !result.getData()) { 220 | // throw new SystemException(AppHttpCodeEnum.REFUND_FAIL); 221 | // } 222 | // orderInfoService.update(new UpdateWrapper() 223 | // .set("status", AppHttpCodeEnum.DONE_REFUND.getMsg()) 224 | // .eq("order_no", orderInfo.getOrderNo())); 225 | } 226 | 227 | @Override 228 | @Transactional 229 | public OrderInfo createOrderFromShopCart(Long userId, List productId, String expireTime) { 230 | 231 | // 0. 查出来商品的信息 232 | List shopCarts = shopCartService 233 | .list(Wrappers.lambdaQuery() 234 | .eq(ShopCart::getUserId, userId).in(ShopCart::getProductId, productId)); 235 | 236 | if (shopCarts.isEmpty()) { 237 | throw new SystemException(AppHttpCodeEnum.PRODUCT_NOT_EXIST); 238 | } 239 | 240 | double totalPrice = shopCarts 241 | .stream() 242 | .mapToDouble(shopCart -> { 243 | return shopCart.getProductCount() * shopCart.getProductPrice(); 244 | }) 245 | .sum(); 246 | 247 | // 1. 生成订单 248 | OrderInfo orderInfo = OrderInfo.builder() 249 | .orderNo(String.valueOf(IdGenerateUtil.get().nextId())) 250 | .userId(userId) 251 | .createDate(new Date()) 252 | .payPrice(totalPrice) 253 | .build(); 254 | 255 | save(orderInfo); 256 | 257 | // 2. 向订单-商品表中插入记录 258 | ArrayList orderToProducts = new ArrayList<>(); 259 | for (ShopCart shopCart : shopCarts) { 260 | OrderToProduct orderToProduct = OrderToProduct.builder() 261 | .orderNo(orderInfo.getOrderNo()) 262 | .productId(shopCart.getProductId()) 263 | .productCount(shopCart.getProductCount()) 264 | .build(); 265 | orderToProducts.add(orderToProduct); 266 | } 267 | orderToProductService.saveBatch(orderToProducts); 268 | 269 | // 3. 购物车商品删除 270 | List list = shopCarts.stream().map(ShopCart::getId).collect(Collectors.toList()); 271 | shopCartService.removeBatchByIds(list); 272 | 273 | log.error("下单成功....." + DateTimeUtils.getCurrentDateTime() + " ===== 订单号:" + orderInfo.getOrderNo()); 274 | //4. 订单定时取消 275 | rabbitTemplate.convertAndSend(RabbitMQConfig.PLUGINS_EXCHANGE, "plugins.timeless", orderInfo, msg -> { 276 | msg.getMessageProperties().setDelay(Integer.valueOf(expireTime)); 277 | return msg; 278 | }); 279 | 280 | return orderInfo; 281 | } 282 | } 283 | 284 | -------------------------------------------------------------------------------- /docker/mysql/db/nacos_config.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | 4 | Source Server : 1 5 | Source Server Type : MySQL 6 | Source Server Version : 80030 7 | Source Host : localhost:3306 8 | Source Schema : nacos_config 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 80030 12 | File Encoding : 65001 13 | 14 | Date: 17/07/2023 00:45:15 15 | */ 16 | 17 | DROP DATABASE IF EXISTS `nacos_config`; 18 | 19 | CREATE DATABASE `nacos_config` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; 20 | 21 | USE `nacos_config`; 22 | 23 | 24 | SET NAMES utf8mb4; 25 | SET FOREIGN_KEY_CHECKS = 0; 26 | 27 | -- ---------------------------- 28 | -- Table structure for config_info 29 | -- ---------------------------- 30 | DROP TABLE IF EXISTS `config_info`; 31 | CREATE TABLE `config_info` ( 32 | `id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT 'id', 33 | `data_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT 'data_id', 34 | `group_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL, 35 | `content` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT 'content', 36 | `md5` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL COMMENT 'md5', 37 | `gmt_create` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间', 38 | `gmt_modified` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '修改时间', 39 | `src_user` text CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL COMMENT 'source user', 40 | `src_ip` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL COMMENT 'source ip', 41 | `app_name` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL, 42 | `tenant_id` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT '' COMMENT '租户字段', 43 | `c_desc` varchar(256) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL, 44 | `c_use` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL, 45 | `effect` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL, 46 | `type` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL, 47 | `c_schema` text CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL, 48 | PRIMARY KEY (`id`) USING BTREE, 49 | UNIQUE INDEX `uk_configinfo_datagrouptenant`(`data_id`, `group_id`, `tenant_id`) USING BTREE 50 | ) ENGINE = InnoDB AUTO_INCREMENT = 47 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_bin COMMENT = 'config_info' ROW_FORMAT = Dynamic; 51 | 52 | -- ---------------------------- 53 | -- Records of config_info 54 | -- ---------------------------- 55 | 56 | -- ---------------------------- 57 | -- Table structure for config_info_aggr 58 | -- ---------------------------- 59 | DROP TABLE IF EXISTS `config_info_aggr`; 60 | CREATE TABLE `config_info_aggr` ( 61 | `id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT 'id', 62 | `data_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT 'data_id', 63 | `group_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT 'group_id', 64 | `datum_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT 'datum_id', 65 | `content` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT '内容', 66 | `gmt_modified` datetime(0) NOT NULL COMMENT '修改时间', 67 | `app_name` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL, 68 | `tenant_id` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT '' COMMENT '租户字段', 69 | PRIMARY KEY (`id`) USING BTREE, 70 | UNIQUE INDEX `uk_configinfoaggr_datagrouptenantdatum`(`data_id`, `group_id`, `tenant_id`, `datum_id`) USING BTREE 71 | ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_bin COMMENT = '增加租户字段' ROW_FORMAT = Dynamic; 72 | 73 | -- ---------------------------- 74 | -- Records of config_info_aggr 75 | -- ---------------------------- 76 | 77 | -- ---------------------------- 78 | -- Table structure for config_info_beta 79 | -- ---------------------------- 80 | DROP TABLE IF EXISTS `config_info_beta`; 81 | CREATE TABLE `config_info_beta` ( 82 | `id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT 'id', 83 | `data_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT 'data_id', 84 | `group_id` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT 'group_id', 85 | `app_name` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL COMMENT 'app_name', 86 | `content` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT 'content', 87 | `beta_ips` varchar(1024) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL COMMENT 'betaIps', 88 | `md5` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL COMMENT 'md5', 89 | `gmt_create` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间', 90 | `gmt_modified` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '修改时间', 91 | `src_user` text CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL COMMENT 'source user', 92 | `src_ip` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL COMMENT 'source ip', 93 | `tenant_id` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT '' COMMENT '租户字段', 94 | PRIMARY KEY (`id`) USING BTREE, 95 | UNIQUE INDEX `uk_configinfobeta_datagrouptenant`(`data_id`, `group_id`, `tenant_id`) USING BTREE 96 | ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_bin COMMENT = 'config_info_beta' ROW_FORMAT = Dynamic; 97 | 98 | -- ---------------------------- 99 | -- Records of config_info_beta 100 | -- ---------------------------- 101 | 102 | -- ---------------------------- 103 | -- Table structure for config_info_tag 104 | -- ---------------------------- 105 | DROP TABLE IF EXISTS `config_info_tag`; 106 | CREATE TABLE `config_info_tag` ( 107 | `id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT 'id', 108 | `data_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT 'data_id', 109 | `group_id` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT 'group_id', 110 | `tenant_id` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT '' COMMENT 'tenant_id', 111 | `tag_id` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT 'tag_id', 112 | `app_name` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL COMMENT 'app_name', 113 | `content` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT 'content', 114 | `md5` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL COMMENT 'md5', 115 | `gmt_create` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间', 116 | `gmt_modified` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '修改时间', 117 | `src_user` text CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL COMMENT 'source user', 118 | `src_ip` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL COMMENT 'source ip', 119 | PRIMARY KEY (`id`) USING BTREE, 120 | UNIQUE INDEX `uk_configinfotag_datagrouptenanttag`(`data_id`, `group_id`, `tenant_id`, `tag_id`) USING BTREE 121 | ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_bin COMMENT = 'config_info_tag' ROW_FORMAT = Dynamic; 122 | 123 | -- ---------------------------- 124 | -- Records of config_info_tag 125 | -- ---------------------------- 126 | 127 | -- ---------------------------- 128 | -- Table structure for config_tags_relation 129 | -- ---------------------------- 130 | DROP TABLE IF EXISTS `config_tags_relation`; 131 | CREATE TABLE `config_tags_relation` ( 132 | `id` bigint(0) NOT NULL COMMENT 'id', 133 | `tag_name` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT 'tag_name', 134 | `tag_type` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL COMMENT 'tag_type', 135 | `data_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT 'data_id', 136 | `group_id` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT 'group_id', 137 | `tenant_id` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT '' COMMENT 'tenant_id', 138 | `nid` bigint(0) NOT NULL AUTO_INCREMENT, 139 | PRIMARY KEY (`nid`) USING BTREE, 140 | UNIQUE INDEX `uk_configtagrelation_configidtag`(`id`, `tag_name`, `tag_type`) USING BTREE, 141 | INDEX `idx_tenant_id`(`tenant_id`) USING BTREE 142 | ) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_bin COMMENT = 'config_tag_relation' ROW_FORMAT = Dynamic; 143 | 144 | -- ---------------------------- 145 | -- Records of config_tags_relation 146 | -- ---------------------------- 147 | 148 | -- ---------------------------- 149 | -- Table structure for group_capacity 150 | -- ---------------------------- 151 | DROP TABLE IF EXISTS `group_capacity`; 152 | CREATE TABLE `group_capacity` ( 153 | `id` bigint(0) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID', 154 | `group_id` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群', 155 | `quota` int(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT '配额,0表示使用默认值', 156 | `usage` int(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT '使用量', 157 | `max_size` int(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT '单个配置大小上限,单位为字节,0表示使用默认值', 158 | `max_aggr_count` int(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT '聚合子配置最大个数,,0表示使用默认值', 159 | `max_aggr_size` int(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值', 160 | `max_history_count` int(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT '最大变更历史数量', 161 | `gmt_create` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间', 162 | `gmt_modified` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '修改时间', 163 | PRIMARY KEY (`id`) USING BTREE, 164 | UNIQUE INDEX `uk_group_id`(`group_id`) USING BTREE 165 | ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_bin COMMENT = '集群、各Group容量信息表' ROW_FORMAT = Dynamic; 166 | 167 | -- ---------------------------- 168 | -- Records of group_capacity 169 | -- ---------------------------- 170 | 171 | -- ---------------------------- 172 | -- Table structure for his_config_info 173 | -- ---------------------------- 174 | DROP TABLE IF EXISTS `his_config_info`; 175 | CREATE TABLE `his_config_info` ( 176 | `id` bigint(0) UNSIGNED NOT NULL, 177 | `nid` bigint(0) UNSIGNED NOT NULL AUTO_INCREMENT, 178 | `data_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL, 179 | `group_id` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL, 180 | `app_name` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL COMMENT 'app_name', 181 | `content` longtext CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL, 182 | `md5` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL, 183 | `gmt_create` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0), 184 | `gmt_modified` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0), 185 | `src_user` text CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL, 186 | `src_ip` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL, 187 | `op_type` char(10) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL, 188 | `tenant_id` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT '' COMMENT '租户字段', 189 | PRIMARY KEY (`nid`) USING BTREE, 190 | INDEX `idx_gmt_create`(`gmt_create`) USING BTREE, 191 | INDEX `idx_gmt_modified`(`gmt_modified`) USING BTREE, 192 | INDEX `idx_did`(`data_id`) USING BTREE 193 | ) ENGINE = InnoDB AUTO_INCREMENT = 60 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_bin COMMENT = '多租户改造' ROW_FORMAT = Dynamic; 194 | 195 | -- ---------------------------- 196 | -- Records of his_config_info 197 | -- ---------------------------- 198 | INSERT INTO `his_config_info` VALUES (5, 48, 'api-gateway-dev.yaml', 'DEFAULT_GROUP', '', 'server:\r\n port: 9000\r\nspring:\r\n cloud:\r\n gateway:\r\n discovery:\r\n locator:\r\n enabled: true # 让gateway可以发现nacos中的微服务\r\n routes:\r\n - id: uaa_route\r\n uri: lb://uaa-service # lb指的是从nacos中按照名称获取微服务,并遵循负载均 衡策略\r\n predicates:\r\n - Path=/uaa/**\r\n filters:\r\n - SorderPrefix=1\r\n - id: product_route\r\n uri: lb://product-service \r\n predicates:\r\n - Path=/product/**\r\n filters:\r\n - SorderPrefix=1\r\n - id: seckill_route\r\n uri: lb://seckill-service \r\n predicates:\r\n - Path=/seckill/**\r\n filters:\r\n - SorderPrefix=1\r\n - id: ws_route\r\n uri: lb://websocket-service \r\n predicates:\r\n - Path=/ws/**\r\n filters:\r\n - SorderPrefix=1\r\n\r\n', 'c9081e664851ac49d802a5e56bcc446b', '2023-07-09 00:56:54', '2023-07-09 00:56:54', NULL, '0:0:0:0:0:0:0:1', 'D', ''); 199 | INSERT INTO `his_config_info` VALUES (6, 49, 'uaa-service-dev.yaml', 'DEFAULT_GROUP', '', 'server:\n port: 8031\nspring:\n datasource:\n url: jdbc:mysql://localhost:3306/shop-uaa?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8\n driverClassName: com.mysql.cj.jdbc.Driver\n type: com.alibaba.druid.pool.DruidDataSource\n username: root\n password: root\n maxActive: 1000\n initialSize: 100\n maxWait: 60000\n minIdle: 500\nmybatis:\n configuration:\n default-fetch-size: 100\n default-statement-timeout: 3000\n map-underscore-to-camel-case: true\n mapperLocations: classpath:cn/timeless/mapper/*Mapper.xml\nribbon:\n eager-load:\n enabled: true\nrocketmq:\n producer:\n group: uaa-group', 'b2bbc4e9bcdd87f8499feab1269e0a8c', '2023-07-09 00:56:54', '2023-07-09 00:56:54', NULL, '0:0:0:0:0:0:0:1', 'D', ''); 200 | INSERT INTO `his_config_info` VALUES (7, 50, 'redis-config-dev.yaml', 'DEFAULT_GROUP', '', 'spring: \n redis:\n host: 127.0.0.1 # 你的redis的Ip地址\n port: 6379', '32eff913945924820597fdfe25f81e90', '2023-07-09 00:56:54', '2023-07-09 00:56:54', NULL, '0:0:0:0:0:0:0:1', 'D', ''); 201 | INSERT INTO `his_config_info` VALUES (8, 51, 'rocketmq-config-dev.yaml', 'DEFAULT_GROUP', '', 'rocketmq:\n name-server: 192.168.111.100:9876 # 你的RocketMQ的Ip地址:9876', '3729b6789e482b9a855c4352121ecb32', '2023-07-09 00:56:54', '2023-07-09 00:56:54', NULL, '0:0:0:0:0:0:0:1', 'D', ''); 202 | INSERT INTO `his_config_info` VALUES (9, 52, 'product-service-dev.yaml', 'DEFAULT_GROUP', '', 'server:\n port: 8041\nspring:\n datasource:\n url: jdbc:mysql://localhost:3306/shop-product?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8\n driverClassName: com.mysql.cj.jdbc.Driver\n type: com.alibaba.druid.pool.DruidDataSource\n username: root\n password: root\n maxActive: 1000\n initialSize: 100\n maxWait: 60000\n minIdle: 500\nmybatis:\n configuration:\n default-fetch-size: 100\n default-statement-timeout: 3000\n map-underscore-to-camel-case: true\n mapperLocations: classpath:cn/timeless/mapper/*Mapper.xml\nribbon:\n eager-load:\n enabled: true', '16c090dfafc279055e8734cd84ba84c9', '2023-07-09 00:56:54', '2023-07-09 00:56:54', NULL, '0:0:0:0:0:0:0:1', 'D', ''); 203 | INSERT INTO `his_config_info` VALUES (10, 53, 'seckill-service-dev.yaml', 'DEFAULT_GROUP', '', 'server:\n port: 8061\nspring:\n datasource:\n url: jdbc:mysql://localhost:3306/shop-seckill?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8\n driverClassName: com.mysql.cj.jdbc.Driver\n type: com.alibaba.druid.pool.DruidDataSource\n username: root\n password: root\n maxActive: 1000\n initialSize: 100\n maxWait: 60000\n minIdle: 500\nmybatis:\n configuration:\n default-fetch-size: 100\n default-statement-timeout: 3000\n map-underscore-to-camel-case: true\n mapperLocations: classpath:cn/timeless/mapper/*Mapper.xml\nribbon:\n eager-load:\n enabled: true\n ReadTimeout: 10000 \n ConnectTimeout: 10000 \n MaxAutoRetries: 0 \n MaxAutoRetriesNextServer: 0 \nrocketmq:\n producer:\n group: seckill-group\n\npay:\n returnUrl: http://8bgfx5.natappfree.cc/seckill/orderPay/returnUrl\n notifyUrl: http://8bgfx5.natappfree.cc/seckill/orderPay/notifyUrl\n frontEndPayUrl: http://localhost/order_detail.html?orderNo=\n errorUrl: http://localhost/50x.html\nfeign:\n sentinel:\n enabled: true', '5b3dd74adecf1a24754a29575fe19923', '2023-07-09 00:56:54', '2023-07-09 00:56:54', NULL, '0:0:0:0:0:0:0:1', 'D', ''); 204 | INSERT INTO `his_config_info` VALUES (11, 54, 'intergral-service-dev.yaml', 'DEFAULT_GROUP', '', 'server:\n port: 8071\nspring:\n datasource:\n url: jdbc:mysql://localhost:3306/shop-intergral?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8\n driverClassName: com.mysql.cj.jdbc.Driver\n type: com.alibaba.druid.pool.DruidDataSource\n username: root\n password: root\n maxActive: 1000\n initialSize: 100\n maxWait: 60000\n minIdle: 500\nmybatis:\n configuration:\n default-fetch-size: 100\n default-statement-timeout: 3000\n map-underscore-to-camel-case: true\n mapperLocations: classpath:cn/timeless/mapper/*Mapper.xml\nribbon:\n eager-load:\n enabled: true', 'a4e96bdc25506cda36b415f6f1ae6e77', '2023-07-09 00:56:54', '2023-07-09 00:56:54', NULL, '0:0:0:0:0:0:0:1', 'D', ''); 205 | INSERT INTO `his_config_info` VALUES (12, 55, 'job-service-dev.yaml', 'DEFAULT_GROUP', '', 'server:\n port: 8081\nfeign:\n sentinel:\n enabled: true\nribbon:\n eager-load:\n enabled: true\n ReadTimeout: 10000 \n ConnectTimeout: 10000 \n MaxAutoRetries: 0 \n MaxAutoRetriesNextServer: 0 \n# elasticjob:\n# zookeeper-url: 192.168.111.100:2181 # 你的zookeeper的Ip地址:2181\n# group-name: shop-job-group\n# jobCron:\n# initSeckillProduct: 0 0/1 * * * ?\n# userCache: 0 0/1 * * * ?\nxxl:\n job:\n admin:\n addresses: http://localhost:4567/xxl-job-admin\n executor:\n appname: productHandler\n address: \"\"\n ip: \"\"\n port: 8585\n logpath: D:\\\\java\\\\code\\\\陈天狼_微服务实战\\\\xxl_job_log\n logretentiondays: 30\n accessToken: default_token', 'bc789b6e7296e03efa4af030697dbfc3', '2023-07-09 00:56:54', '2023-07-09 00:56:54', NULL, '0:0:0:0:0:0:0:1', 'D', ''); 206 | INSERT INTO `his_config_info` VALUES (13, 56, 'websocket-service-dev.yaml', 'DEFAULT_GROUP', '', 'server:\r\n port: 8091\r\nrocketmq:\r\n producer:\r\n group: websocket-group', '37a3d2fb19670379d84bf440bddea79c', '2023-07-09 00:56:54', '2023-07-09 00:56:54', NULL, '0:0:0:0:0:0:0:1', 'D', ''); 207 | INSERT INTO `his_config_info` VALUES (14, 57, 'pay-service-dev.yaml', 'DEFAULT_GROUP', '', 'server:\n port: 8051\nribbon:\n eager-load:\n enabled: true\nrocketmq:\n producer:\n group: pay-group\nalipay:\n app_id: 2021000122674469\n merchant_private_key: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCTAT0pyVIMmEK2rxQFhX5cqKyVJZxA/A952ntyjAPOHPafDOalPc1wY5KBJD59idBp5HDQojYUgH4hLQBm0dU/+Fv9cQTkgIl47ZKPx+hU6zlZJfbyKUXVPhFQEMPddmvcgAfp/M+8cthbVt0Mjcp+x211+V0FPiqnY/fk5qXZjwthjICeEVmVVwtphDSK0UgzU5fhIcTjWxEYtznWhAWr7eYF2yD80jigWHQxiuf/er+HUf0gsCI/C1OxFUzlH9FBFFfBE+Lc4rQtSZ4kGsjFmpfHEPdYDwJ+xc5FISnOtb2htzp3wqR4Q1zvo2b9XSW+rFVxqNPcdxPC5BrgVGLvAgMBAAECggEAJb8fsWccRleiab4y6egJNkmpZvKtWJJgdu1+3T7Oi1IskVKCttNTuRQYPkjMMvf8J/ScczXzpPgJawNfIwemNjLTjBRFKVdH9WErTPgL1CJCK33wFuY6JDM3xtNHN8p8j7XgUli0DrN+kpWPVPXjlQIoPS62j+4SjcDBmmkPFPma8GKUZgGD3uNSNLnKrAorTMUviIYn9p4oR2gJ5EXIZk0KzhViWgI57SHIUx1nfstKnobTtP19aFyBdHgeUq5xxmVfHbBl/6qlOoxaS6ZQZA2U5WUlyQXQS4SlYC3m6tm581hTN4+J6D4DEx7MxA0cG4L3jyoB1tMC+1rTZsCoAQKBgQD7t2PDDrW0TpX52ZM6ZrFanP398qJp0f3AOz5Dko4YiK2yEZLZoM21uGYsC+Rr/ijg3RuqLPzNZFDFvsbdnO9CppIA8/f1O5jHAU+WdOCvvjLH8ITHRR+cFd+a7vJMiF4KeMXrdDYmyXroe2WQWluSpQJWoJGgqNrI/Jj9H8h9AQKBgQCVgauO4N6B6nSfht/jHJD/zD2up+J5e0YWbKZl3ITa4ZIXba1Yd8HCQvvfxiYcKXBlPVPsqg154m19UcHyvQMImNF7sPFGxpSjR9KGYEtB0uwGzVP5Vf+/t/28e29Ru2j4JgxJmZC6alOxOhmx1uIsUELtkEKxdGdil5feQrSv7wKBgEr9A1dk1nT0xHE+hEHtvgBErNYupnvn9zSBcbcnvfVJIpXd7mWvJhlw6d9NW6tgeEUlGczEwjteG0IN56i1zAGLJgvqooAIVSdUGKW2BAnXG8G8wZGr4hLZ4VeCv+RU688q06ulX0yG0XrY6BTtFkLFrcCo7TlzlFo0Bk/CXDQBAoGAFadWRbdkAsUGZySNwMMebS4TsPw33QhxukW6Q/6Qb3NESYhlFu8hbRVujZaRZnrKAJ/FS/3FPeYBnaj9wj6F9fTMQnH6QBn65Ts9zqCbxcjazpFmnDJMfGQVp/rowJm4NlisZuyK/bTwsjaMCinpUCm/x9ChGDqXzrvODy7yJJkCgYEA+fr22r7IX+CqFE7QlfMJ44ePAOQfx8zyN18RSzJzhUoS9W4iwnmL2x90Ak+XUC+7gm8HCL7GAR6KHLTO0miRAk6PNRyL2cStWg9MpXoZmxc13fbSXO2TT9dXyxifRnT1RYYqwiHzkY8Ni9oGC4jMzgugua4zEnC9yAbWXA/n1e8=\n alipay_public_key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuTGdCrOT/z0FM3wT68fHNWH38mGrkn9dgr/9xAUmm8puQYJr0yKhl4CGZLYeg/IC+w9sSVdj83kt+vp6lwrgNtSlNLf0gSucFMtRu5rzq3DgPgHZcsruLLaa/Gn4A0wzVL+XxCE5EGm/jYw9MIrP9tCiYKhLqqAlnAYlSBmslGi2SRj7Ch+BweKv1Ki1D2jeZC0O86rxNJKuHiCnk79RQ0rgvbB56gd8FQHNX8EVXq8xH/4OOu4v9IPtnMPBrhLNct/rMqo/JyOQI5QjnSk43yc5Xrox371E0Qrzf6zjoQPF0bzB+nzA3UwMuA82knvk7STLqJy21NBq7hT6r1gPIQIDAQAB\n sign_type: RSA2\n charset: utf-8\n gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do', 'dd87e07f2b01a825aa7451a7d780cff7', '2023-07-09 00:56:54', '2023-07-09 00:56:54', NULL, '0:0:0:0:0:0:0:1', 'D', ''); 208 | INSERT INTO `his_config_info` VALUES (15, 58, 'nacos-discovery-config-dev.yaml', 'DEFAULT_GROUP', '', 'spring:\n cloud:\n nacos:\n discovery:\n server-addr: 127.0.0.1:8848', '68118ee8954f6bd1571b02f02dd3c153', '2023-07-09 00:56:56', '2023-07-09 00:56:57', NULL, '0:0:0:0:0:0:0:1', 'D', ''); 209 | INSERT INTO `his_config_info` VALUES (16, 59, 'canal-service-dev.yaml', 'DEFAULT_GROUP', '', 'server:\n port: 8021\ncanal:\n server: 192.168.111.100:11111\n destination: example\nlogging:\n level:\n root: info\n top:\n javatool:\n canal:\n client:\n client:\n AbstractCanalClient: error\n\n\n', '5a8e7410e19c19d78f50e0a4af534898', '2023-07-09 00:56:56', '2023-07-09 00:56:57', NULL, '0:0:0:0:0:0:0:1', 'D', ''); 210 | 211 | -- ---------------------------- 212 | -- Table structure for permissions 213 | -- ---------------------------- 214 | DROP TABLE IF EXISTS `permissions`; 215 | CREATE TABLE `permissions` ( 216 | `role` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 217 | `resource` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 218 | `action` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 219 | UNIQUE INDEX `uk_role_permission`(`role`, `resource`, `action`) USING BTREE 220 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 221 | 222 | -- ---------------------------- 223 | -- Records of permissions 224 | -- ---------------------------- 225 | 226 | -- ---------------------------- 227 | -- Table structure for roles 228 | -- ---------------------------- 229 | DROP TABLE IF EXISTS `roles`; 230 | CREATE TABLE `roles` ( 231 | `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 232 | `role` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 233 | UNIQUE INDEX `idx_user_role`(`username`, `role`) USING BTREE 234 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 235 | 236 | -- ---------------------------- 237 | -- Records of roles 238 | -- ---------------------------- 239 | INSERT INTO `roles` VALUES ('nacos', 'ROLE_ADMIN'); 240 | 241 | -- ---------------------------- 242 | -- Table structure for tenant_capacity 243 | -- ---------------------------- 244 | DROP TABLE IF EXISTS `tenant_capacity`; 245 | CREATE TABLE `tenant_capacity` ( 246 | `id` bigint(0) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID', 247 | `tenant_id` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL DEFAULT '' COMMENT 'Tenant ID', 248 | `quota` int(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT '配额,0表示使用默认值', 249 | `usage` int(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT '使用量', 250 | `max_size` int(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT '单个配置大小上限,单位为字节,0表示使用默认值', 251 | `max_aggr_count` int(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT '聚合子配置最大个数', 252 | `max_aggr_size` int(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值', 253 | `max_history_count` int(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT '最大变更历史数量', 254 | `gmt_create` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间', 255 | `gmt_modified` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '修改时间', 256 | PRIMARY KEY (`id`) USING BTREE, 257 | UNIQUE INDEX `uk_tenant_id`(`tenant_id`) USING BTREE 258 | ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_bin COMMENT = '租户容量信息表' ROW_FORMAT = Dynamic; 259 | 260 | -- ---------------------------- 261 | -- Records of tenant_capacity 262 | -- ---------------------------- 263 | 264 | -- ---------------------------- 265 | -- Table structure for tenant_info 266 | -- ---------------------------- 267 | DROP TABLE IF EXISTS `tenant_info`; 268 | CREATE TABLE `tenant_info` ( 269 | `id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT 'id', 270 | `kp` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NOT NULL COMMENT 'kp', 271 | `tenant_id` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT '' COMMENT 'tenant_id', 272 | `tenant_name` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT '' COMMENT 'tenant_name', 273 | `tenant_desc` varchar(256) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL COMMENT 'tenant_desc', 274 | `create_source` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_bin NULL DEFAULT NULL COMMENT 'create_source', 275 | `gmt_create` bigint(0) NOT NULL COMMENT '创建时间', 276 | `gmt_modified` bigint(0) NOT NULL COMMENT '修改时间', 277 | PRIMARY KEY (`id`) USING BTREE, 278 | UNIQUE INDEX `uk_tenant_info_kptenantid`(`kp`, `tenant_id`) USING BTREE, 279 | INDEX `idx_tenant_id`(`tenant_id`) USING BTREE 280 | ) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_bin COMMENT = 'tenant_info' ROW_FORMAT = Dynamic; 281 | 282 | -- ---------------------------- 283 | -- Records of tenant_info 284 | -- ---------------------------- 285 | 286 | -- ---------------------------- 287 | -- Table structure for users 288 | -- ---------------------------- 289 | DROP TABLE IF EXISTS `users`; 290 | CREATE TABLE `users` ( 291 | `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 292 | `password` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 293 | `enabled` tinyint(1) NOT NULL, 294 | PRIMARY KEY (`username`) USING BTREE 295 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 296 | 297 | -- ---------------------------- 298 | -- Records of users 299 | -- ---------------------------- 300 | INSERT INTO `users` VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', 1); 301 | 302 | SET FOREIGN_KEY_CHECKS = 1; 303 | --------------------------------------------------------------------------------