1、部分效果演示
- 车辆列表

 - 租车详情

 - 订单列表

 - jmeter压力测试

 - 抢单错误信息持久化到redis

 - 控制台信息

 
2、技术栈
| 功能或特点 | 名称 | 版本 | 
|---|---|---|
| 父项目依赖 | spring-boot-starter-parent | 2.2.5.RELEASE | 
| 微服务架构 | spring-cloud-dependencies | Hoxton.RELEASE | 
| admin监控服务 | spring-boot-admin-starter-server | 2.2.4 | 
| eureka管理服务 | spring-cloud-starter-netflix-eureka-server | 2.2.0.RELEASE | 
| config配置服务 | spring-cloud-config-server | 2.2.0.RELEASE | 
| gateway网关服务 | spring-cloud-starter-gateway | 2.2.0.RELEASE | 
| 链路追踪服务 | spring-cloud-starter-zipkin | 2.2.0.RELEASE | 
| redis限流 | spring-boot-starter-data-redis-reactive | 2.2.5.RELEASE | 
| 组件调用(已集成熔断,负载均衡) | spring-cloud-starter-openfeign | 2.2.0.RELEASE | 
| hutool工具类 | hutool-all | 5.8.22 | 
| 数据库curd | mybatis-plus-boot-starter | 3.5.3.2 | 
| 短信榛子云短信 | zhenzisms | 2.0.2 | 
| email邮件发送 | spring-boot-starter-mail | 2.2.5.RELEASE | 
| mq消息队列 | rocketmq-spring-boot-starter | 2.2.1 | 
| 阿里云oos对象储存 | aliyun-sdk-oss | 3.15.1 | 
| 支付宝支付 | alipay-sdk-java | 4.38.111.ALL | 
| 分布式图片管理 | minio | 8.2.2 | 
3、后端集成
3.2、用户登录
- UserController.java
 
package com.llh.controller;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWTUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.llh.domain.User;
import com.llh.service.UserService;
import com.llh.utils.ZzyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
 * User: lilinhan
 * DateTime: 2023/10/20 11:57
 */
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    UserService userService;
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    JavaMailSender javaMailSender;
    @RequestMapping("/test")
    public String test(){
        return "hhhhhh";
    }
    // 验证码接口
    @RequestMapping("/getCode")
    public void getCode(HttpServletResponse response) throws IOException {
        CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(150, 60, 4, 20);
        // 重新生成code
        captcha.createCode();
        captcha.write(response.getOutputStream());
        redisTemplate.opsForValue().set("yzm", captcha.getCode(), 1, TimeUnit.MINUTES);
    }
    @RequestMapping("/sendSms")
    public Map<String,Object> sendSms(String phone){
        Map<String,Object> map = new HashMap<>();
        boolean tel = phone.matches("(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}");
        if (!tel) {
            map.put("code", 1005);
            map.put("msg", "手机号格式错误");
            return map;
        }
        String code = RandomUtil.randomNumbers(4);
        int time = 5;
        boolean b = ZzyUtils.sendSms(phone, code, time);
        if (!b) {
            map.put("code", 1005);
            map.put("msg", "短信发送失败");
            return map;
        } else {
            redisTemplate.opsForValue().set(phone, code, time, TimeUnit.MINUTES);
            map.put("code", 1001);
            map.put("msg", "短信发送成功");
            return map;
        }
    }
    @RequestMapping("/login")
    public Map<String,Object> login(User user){
        Map<String,Object> map = new HashMap<>();
        QueryWrapper  wrapper = new QueryWrapper();
        wrapper.eq("name",user.getName());
        User userDB = userService.getOne(wrapper);
        if(userDB==null){
            map.put("code",1005);
            map.put("msg","该用户不存在");
            return map;
        }
        String md5Hex = DigestUtil.md5Hex(user.getPwd() + userDB.getSalt());
        if(!userDB.getPwd().equals(md5Hex)){
            map.put("code",1005);
            map.put("msg","密码错误");
            return map;
        }
        String code = (String) redisTemplate.opsForValue().get("yzm");
        if(StrUtil.isNotEmpty(code)){
            if (!code.equalsIgnoreCase(user.getCode())) {
                map.put("code", 1005);
                map.put("msg", "验证码错误");
                return map;
            }
        }else {
            map.put("code", 1005);
            map.put("msg", "验证码已过期");
            return map;
        }
        Map<String, Object> map1 = new HashMap<>();
        map1.put("user", JSONUtil.toJsonStr(userDB));
        map1.put("loginName", userDB.getName());
        map1.put("userId", userDB.getId());
        String token = JWTUtil.createToken(map1, "llh".getBytes());
        map.put("token", token);
        map.put("loginName", userDB.getName());
        map.put("userId", userDB.getId());
        map.put("code", 1001);
        map.put("msg", "登录成功!");
        return map;
    }
    @RequestMapping("/phoneLogin")
    public Map<String,Object> phoneLogin(User user){
        Map<String,Object> map = new HashMap<>();
        QueryWrapper  wrapper = new QueryWrapper();
        wrapper.eq("name",user.getName());
        User userDB = userService.getOne(wrapper);
        if(userDB==null){
            map.put("code",1005);
            map.put("msg","该用户不存在");
            return map;
        }
        String md5Hex = DigestUtil.md5Hex(user.getPwd() + userDB.getSalt());
        if(!userDB.getPwd().equals(md5Hex)){
            map.put("code",1005);
            map.put("msg","密码错误");
            return map;
        }
        String code = (String) redisTemplate.opsForValue().get(user.getPhone());
        if(StrUtil.isNotEmpty(code)){
            if (!code.equals(user.getCode())) {
                map.put("code", 1005);
                map.put("msg", "验证码错误");
                return map;
            }
        }else {
            map.put("code", 1005);
            map.put("msg", "验证码已过期");
            return map;
        }
        Map<String, Object> map1 = new HashMap<>();
        map1.put("user", JSONUtil.toJsonStr(userDB));
        map1.put("loginName", userDB.getName());
        map1.put("userId", userDB.getId());
        String token = JWTUtil.createToken(map1, "llh".getBytes());
        map.put("token", token);
        map.put("loginName", userDB.getName());
        map.put("userId", userDB.getId());
        map.put("code", 1001);
        map.put("msg", "登录成功!");
        return map;
    }
    // 注册接口
    @RequestMapping("/register")
    public Map<String, Object> register(User user) {
        Map<String, Object> map = new HashMap<>();
        boolean qq = user.getEmail().matches("[1-9][0-9]{4,10}");
//        boolean email = user.getEmail().matches("^[A-Za-z0-9]+([._%+-][A-Za-z0-9]+)*@[A-Za-z0-9]+(\\\\.[A-Za-z0-9]+)*(\\\\.[A-Za-z]{2,})$");
        if(!qq){
            map.put("code", 1005);
            map.put("msg", "QQ号格式错误");
            return map;
        }
        boolean phone2 = user.getPhone().matches("(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}");
        if (!phone2) {
            map.put("code", 1005);
            map.put("msg", "手机号格式错误");
            return map;
        }
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("phone",user.getPhone());
        User userDB = userService.getOne(wrapper);
        if(userDB!=null){
            map.put("code", 1005);
            map.put("msg", "手机号已存在");
            return map;
        }
        if(StrUtil.isNotBlank(user.getCode())){
            String code = (String) redisTemplate.opsForValue().get("yzm");
            if (!code.equalsIgnoreCase(user.getCode())) {
                map.put("code", 1005);
                map.put("msg", "验证码错误");
                return map;
            }
        }else {
            map.put("code", 1005);
            map.put("msg", "验证码已过期");
            return map;
        }
        // 发送短信
//        sendSms(user.getPhone());
        String initPwd = "123";
        String initName = "张三";
        String salt = UUID.randomUUID().toString().substring(0, 4);
        // 邮件发送
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setFrom("llh.fool@qq.com");
        mailMessage.setTo(user.getEmail()+"@qq.com");
        mailMessage.setSentDate(new Date());
        mailMessage.setSubject("注册成功提示");
        mailMessage.setText("恭喜:"+user.getEmail()+"注册成功!\n初始用户名:"+initName+",初始密码为:"+initPwd);
        javaMailSender.send(mailMessage);
        user.setPwd(DigestUtil.md5Hex(initPwd + salt));
        user.setName(initName);
        user.setSalt(salt);
        boolean save = userService.save(user);
        if (save) {
            map.put("code", 1001);
            map.put("msg", "注册成功");
            return map;
        }
        return map;
    }
}3.2、车辆基本crud
- 车辆curd,CarController.java
 
package com.llh.controller;
import cn.hutool.core.lang.Snowflake;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.llh.domain.Car;
import com.llh.service.CarService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
 * User: lilinhan
 * DateTime: 2023/10/30 18:39
 */
@RestController
@RequestMapping("/car")
public class CarController {
    @Autowired
    CarService carService;
    // 更新车辆
    @RequestMapping("/update")
    public Map<String,Object> update(Car car){
        Map<String,Object> map = new HashMap<>();
        boolean b = carService.updateById(car);
        if(b){
            map.put("flag",b);
            map.put("msg","修改成功");
            return map;
        }else {
            map.put("flag",b);
            map.put("msg","修改失败");
            return map;
        }
    }
    // 车辆下架
    @RequestMapping("/del")
    public Map<String,Object> del(Integer id){
        Map<String,Object> map = new HashMap<>();
        boolean b = carService.removeById(id);
        if(b){
            map.put("flag",b);
            map.put("msg","删除成功");
            return map;
        }else {
            map.put("flag",b);
            map.put("msg","删除失败");
            return map;
        }
    }
    // 通过订单编号获取车辆信息
    @RequestMapping("/updateCarByNo")
    public void updateCarByNo(String no){
        carService.updateCarByNo(no);
    }
    // 车辆列表+查询
    @RequestMapping("/list")
    public Page<Car> list(Page<Car> page, Car car){
        return carService.getList(page,car);
    }
    // 车辆录入
    @RequestMapping("/save")
    public Map<String,Object> save(Car car){
        Map<String,Object> map = new HashMap<>();
        // 编号使用雪花算法
        car.setNum(new Snowflake().nextIdStr());
        car.setCreateTime(new Date());
        car.setSpeed("手自一体");
        car.setRent(new BigDecimal(2000));
        boolean save = carService.save(car);
        map.put("flag",save);
        map.put("msg","添加成功");
        return map;
    }
}- 下拉框数据,DistController.java
 
package com.llh.controller;
import com.llh.domain.Dist;
import com.llh.service.DistService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
 * User: lilinhan
 * DateTime: 2023/10/30 18:42
 */
@RestController
@RequestMapping("/dist")
public class DistController {
    @Autowired
    DistService distService;
    @GetMapping("/list")
    public Map<String, List<Dist>> list(){
        Map<String,List<Dist>> map = new HashMap<>();
        List<Dist> list = distService.list();
        // 获取车系、车型、品牌和颜色下拉框
        map.put("models",list.stream().filter(dist -> dist.getDistType().equals("car_model")).collect(Collectors.toList()));
        map.put("brands",list.stream().filter(dist -> dist.getDistType().equals("car_brand")).collect(Collectors.toList()));
        map.put("dists",list.stream().filter(dist -> dist.getDistType().equals("car_dist")).collect(Collectors.toList()));
        map.put("colors",list.stream().filter(dist -> dist.getDistType().equals("car_color")).collect(Collectors.toList()));
        return map;
    }
}3.3、图片上传
- 使用minio上传,UploadController.java
 
package com.llh.controller;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
 * User: lilinhan
 * DateTime: 2023/10/24 10:34
 */
@RestController
@RequestMapping("/upload")
public class UploadController {
    public static final String MINIO_SERVER = "http://127.0.0.1:9000";
    private static final String BUCKET_NAME = "images";
    @RequestMapping("/minio")
    public Map<String, Object> minio(MultipartFile file) {
        Map<String, Object> map = new HashMap<>();
        // 创建客户端
        MinioClient minioClient = MinioClient.builder()
              .endpoint(MINIO_SERVER)      // 访问url
              .credentials("EGQVyIa2qbjWAKZzGZ8T","SqEHCH3elRayyazcTXESAsrozAWTOmtrIX7SssSB")   // 密钥
              .build();
        String fileName = UUID.randomUUID() + "-" + file.getOriginalFilename();
        try {
            // 使用自带方法 putObject
            minioClient.putObject(PutObjectArgs.builder()
                  .bucket(BUCKET_NAME)     // 桶的名称
                  .object(fileName)   // 文件名称
                  .contentType(file.getContentType())     // 文件类型
                  .stream(file.getInputStream(), file.getSize(), -1)        // 文件流传输
                  .build());
            map.put("code",1001);
            map.put("msg","上传成功");
            map.put("url",MINIO_SERVER+"/"+BUCKET_NAME+"/"+fileName);
            return map;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}- 使用阿里云oss对象储存,AlyOssUtils.java
 
package com.llh.utils;
import cn.hutool.core.lang.UUID;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.CannedAccessControlList;
import org.springframework.web.multipart.MultipartFile;
import java.util.HashMap;
import java.util.Map;
/**
 * User: lilinhan
 * DateTime: 2023/11/2 10:07
 */
public class AlyOssUtils {
    private static final String ENDPOINT = "https://oss-cn-beijing.aliyuncs.com";// oss实例的地域节点
    private static final String ACCESS_KEY_ID = "LTAI5t8M6dewZALaTRb3QEC7";// 阿里云密钥ID
    private  static final String ACCESS_KEY_SECRET = "9iG7iCRwATM0cVB8kPxmPSTbhqotqk";// 阿里云密钥
    private static final String BUCKET_NAME = "llh-images";// oss实例的桶名
    /**
     * 图片上传(包含文件夹)
     * @param file 文件
     * @param model   阿里云的文件夹名
     * @return  map集合
     */
    public static Map<String,Object> uploadToModel(MultipartFile file,String model){
        Map<String,Object> map = new HashMap<>();
        String url = "";
        try {
            // 创建ossClient实例
            OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID,ACCESS_KEY_SECRET);
            // 判断oss实例是否存在
            if(!ossClient.doesBucketExist(BUCKET_NAME)){
                // 创建桶名
                ossClient.createBucket(BUCKET_NAME);
                // 设置oss实例的访问权限:公共读
                ossClient.setBucketAcl(BUCKET_NAME, CannedAccessControlList.PublicRead);
            }
            // 获取文件名
            String filename = UUID.randomUUID() + file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
            // 文件根路径
            String key = model+"/"+filename;
            // 将文件上传到阿里云
            ossClient.putObject(BUCKET_NAME,key,file.getInputStream());
            // 获取url地址
            url = "https://"+BUCKET_NAME+"."+ENDPOINT.substring(ENDPOINT.lastIndexOf("//")+2)+"/"+key;
            // 关闭客户端
            ossClient.shutdown();
            map.put("code",1001);
            map.put("url",url);
            map.put("msg","上传成功");
            return map;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 图片上传
     * @param file   上传的文件
     * @return  map集合
     */
    public static Map<String,Object> upload(MultipartFile file){
        Map<String,Object> map = new HashMap<>();
        String url = "";
        try {
            // 创建ossClient实例
            OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID,ACCESS_KEY_SECRET);
            // 判断oss实例是否存在
            if(!ossClient.doesBucketExist(BUCKET_NAME)){
                // 创建桶名
                ossClient.createBucket(BUCKET_NAME);
                // 设置oss实例的访问权限:公共读
                ossClient.setBucketAcl(BUCKET_NAME, CannedAccessControlList.PublicRead);
            }
            // 获取文件名
            String filename = UUID.randomUUID() + file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
            // 将文件上传到阿里云
            ossClient.putObject(BUCKET_NAME,filename,file.getInputStream());
            // 获取url地址
            url = "https://"+BUCKET_NAME+"."+ENDPOINT.substring(ENDPOINT.lastIndexOf("//")+2)+"/"+filename;
            // 关闭客户端
            ossClient.shutdown();
            map.put("code",1001);
            map.put("url",url);
            map.put("msg","上传成功");
            return map;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}3.4、出租功能
- 订单的curd,OrderController.java
 
package com.llh.controller;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.crypto.digest.DigestUtil;
import com.llh.domain.Order;
import com.llh.service.CarService;
import com.llh.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
 * User: lilinhan
 * DateTime: 2023/10/31 15:25
 */
@RestController
@RequestMapping("/order")
public class OrderController {
    @Autowired
    OrderService orderService;
    @Resource
    CarService carService;
    @Autowired
    RedisTemplate redisTemplate;
    // 通过编号更新订单
    @RequestMapping("/updateOrderByNo")
    public void updateOrderByNo(String no){
        orderService.updateOrderByNo(no);
    }
    // 生成订单
    @RequestMapping("/save")
    public Map<String,Object> save(Order order){
        Map<String,Object> map = new HashMap<>();
        String md5Hex = DigestUtil.md5Hex(order + "");
        Boolean b = redisTemplate.opsForValue().setIfAbsent(md5Hex, md5Hex, 1, TimeUnit.MINUTES);
        if(!b){
            map.put("flag",false);
            map.put("msg","一分钟禁止重复提交");
            return map;
        }
        // 订单编号
        String no = new Snowflake().nextIdStr();
        // 计算租期
        long days = DateUtil.betweenDay(order.getCarStart(), order.getCarEnd(), false);
        // 租金的费用
        order.setCarPrice(order.getCarRent().multiply(new BigDecimal(days)));
        order.setNum(no);
        order.setCreateTime(new Date());
        // 录入订单
        boolean save = orderService.save(order);
        // 修改车辆表状态
        carService.updateCarByNo(order.getCarNo());
        map.put("flag",save);
        map.put("msg","生成订单成功");
        return map;
    }
    // 订单列表
    @RequestMapping("/list")
    public List<Order> list(){
        return orderService.list();
    }
}3.5、支付功能
- PayController.java
 
package com.llh.controller;
import cn.hutool.extra.servlet.ServletUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.llh.domain.Order;
import com.llh.service.OrderService;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * User: lilinhan
 * DateTime: 2023/10/31 18:51
 */
@RestController
@RequestMapping("/pay")
public class PayController {
    String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgmJ5v66LB3uJiOa+/J+m1B3sgn1TNqO1Lh3gcfEoY3VIZCzqrGMCRrq0zk+EpOuchCZZJolN72AAWor97aLxCZzsVebhWZNvywCYW0fkbUF25uMKaIEQoNasPnLMToflhyzzmS4bLnamt+kv8xQf6XSM5FerT4J6w2JFFi1Vmmz0gTfGfqU6xpWDCjP6QZ7eOj/ujWvTgtavpG8cnsaqnakl1VeYkMW5jXGsOq16ms5W1/a9IsCw/Oe1Ez2vPKwEjxv+xxNpTdxcQm+Oq7Dc8/gAxkJBUy7SbI88II54MbY7qWDM+xKuY+WigOjNWiX/3m/M7RCrduLOV3Y8aGiBdQIDAQAB";
    @Autowired
    OrderService orderService;
    @Autowired
    RocketMQTemplate rocketMQTemplate;
    @Autowired
    HttpServletResponse servletResponse;
    @Autowired
    HttpServletRequest request;
    // 前往支付
    @RequestMapping("/goPay")
    public void goPay(String no) throws AlipayApiException, IOException {
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.eq("num", no);
        Order orderDB = orderService.getOne(wrapper);
        Message<String> message = MessageBuilder.withPayload(no).build();
        // 异步发送mq
        rocketMQTemplate.asyncSend("car2", message, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                if (SendStatus.SEND_OK.equals(sendResult.getSendStatus())) {
                    System.err.println("发送成功");
                }
            }
            @Override
            public void onException(Throwable e) {
                System.err.println("连接失败");
            }
        });
        String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCLHDjvEn6fjWNOV/SGgkNv7B0AsgTA1WuKypedU2WIJ4hE5ZvJI5NiYgyh/O/HeFPYPhv+P7wv1+ATKGc5yyvb7I2b8/3MZhxFYMgn/4HjgJ9EGGfSSIkM5JqoZFHRuUNu5I1a9PqTW7SaReRdmXs3axVuHd+MYLh9KVGJyAiueAtQwq040EZHHz5lQ9oSj/R7zPEl4jXtXKHq4MC/tRl/wqAYeKwMeati9iUhK9BHYzxeTiV08l6UrMZteQ7duNxJTIg/uLQdgb7DwaWcBgy514Ra50PKmpXlHasIwhXfnW58aSpVjzdnbYu290JsGXW9h5CRp1VxZlqDfeg7zC95AgMBAAECggEAemldWFNIBZLfXiNb07lxKl31r5T3RdEID1vzSsgGQme3LBl4muipWxu4zhrLzRV/gnw1Gehv6xHl5jXZkCvO7nocqq1sGp+IJSzjNP9MByw5+iwXwB2ALE5GIgQVU53ZTw3jrbSBg4ZhSJhUOmFC4iNi+kFjZB4AenMPg1T4/rzqPPI17FO8MsW1CYiRmQcajkSDm37WXEayOaeSUXbDbxwUU96sNYU96kOuS92rX05hiZ+mX4XtKLqPuUVp0OKHe7WVEnf4sQp0nnZV4FjIS1MFk/dG24RfXBNtAkkA9m0NRxC0MTPEc2bwbjJ6bRyaETLMdzEN5d2qgCBYQzwJWQKBgQDQnHtx9Sg43XeSgqssRNaRkqC+naqyccorSfa5MTzxYfU1ByWlI4/SFfqHAbGTv2i6M191tOqffGHRHQUnyv8pSNwEbTUaQ7qZO03KiRc5cKw21U9UPrhUM8Lb5l7W2hrkpcLf5HOHOwqyMy215M84G8AsMb2vFhTD0+zh4fWHWwKBgQCqtf+kvhrO8bQQ69lgow/RB1g06VCM7I8a8WCWeLjbNwJujpcBmZA+ozVydmTsa3RNqaBfcUe6V5qQ8HCfLU4M9mKAd1cZSpP0/gwxjXBanT0pW2IaqfCJnwSZE602hNJhaDcIo0UEa94EK0TD67bpjqbPmrkt8GADjxGj3jfwuwKBgQCxSHrEErL2+IcYdIGhonKyzehbdcRN19QWy8moAocnH7dyNDuyxrD/ufvpZfASfrUyoFv8vR4zIVJDkyUBd0s3O5r3DBP32R0oAbUPbUfWYcGI6+h50L93l3F+zZ1Fe58lNxWQjiX8A5oIbOPo06Wiqjq7lB3+QIavEVgbcprlRwKBgB5D5A3uEkysN3NkjQaAAlbZyX6f+sLxUbHCJ5Cl2Igs6YZ2SzliY1tnjhs7+EqeBmGaeC9ToxbbXzDhH+ZontTXfcEu50c5nJ+rxremTswR4n0JkYwOovGTOrp07Zmj1LSmSFOEtsDA6vysOyXNDYG848Acu0X0vuECpWr7A8nvAoGBAJwEnEs86l0koOSo/DVVaVudJkMXhvl9GT7wG/MiEvknjTWosdVRgquEzGjmE25eK/4epOCONVU7bweloLSLbZ/OGGPGS4jHhJ3Kw/e4KBn4+nCi2g2/7CLfGvaydXmhgRy4qjIATfLceXtXaBNj7HIC1kCwzSphFwPzSM6DDEU8";
        String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgmJ5v66LB3uJiOa+/J+m1B3sgn1TNqO1Lh3gcfEoY3VIZCzqrGMCRrq0zk+EpOuchCZZJolN72AAWor97aLxCZzsVebhWZNvywCYW0fkbUF25uMKaIEQoNasPnLMToflhyzzmS4bLnamt+kv8xQf6XSM5FerT4J6w2JFFi1Vmmz0gTfGfqU6xpWDCjP6QZ7eOj/ujWvTgtavpG8cnsaqnakl1VeYkMW5jXGsOq16ms5W1/a9IsCw/Oe1Ez2vPKwEjxv+xxNpTdxcQm+Oq7Dc8/gAxkJBUy7SbI88II54MbY7qWDM+xKuY+WigOjNWiX/3m/M7RCrduLOV3Y8aGiBdQIDAQAB";
        AlipayConfig alipayConfig = new AlipayConfig();
        alipayConfig.setServerUrl("https://openapi-sandbox.dl.alipaydev.com/gateway.do");
        alipayConfig.setAppId("9021000130615233");
        alipayConfig.setPrivateKey(privateKey);
        alipayConfig.setFormat("json");
        alipayConfig.setAlipayPublicKey(alipayPublicKey);
        alipayConfig.setCharset("UTF-8");
        alipayConfig.setSignType("RSA2");
        AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        request.setReturnUrl("http://127.0.0.1:9999/car-api/pay/callback");
        AlipayTradePagePayModel model = new AlipayTradePagePayModel();
        model.setOutTradeNo(orderDB.getNum());
        model.setTotalAmount(orderDB.getCarPrice() + "");
        model.setSubject(orderDB.getCarNo());
        model.setProductCode("FAST_INSTANT_TRADE_PAY");
        request.setBizModel(model);
        AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
        String body = response.getBody();
        if (response.isSuccess()) {
            System.err.println("调用成功");
            servletResponse.setContentType("text/html;charset=utf8");
            PrintWriter writer = servletResponse.getWriter();
            writer.write(body);
            writer.flush();
            writer.close();
        } else {
            System.err.println("调用失败");
        }
    }
    // 回调方法
    @RequestMapping("/callback")
    public void callback(String out_trade_no) throws IOException, AlipayApiException {
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.eq("num", out_trade_no);
        Order orderDB = orderService.getOne(wrapper);
        orderDB.setStat(1);
        orderDB.setRemark("已支付");
        orderService.updateById(orderDB);
        // 使用hutool把参数转换为map集合
        Map<String, String> params = ServletUtil.getParamMap(request);
        // 验证签名
        boolean signVerified = AlipaySignature.rsaCheckV1(params, alipayPublicKey, "UTF-8", "RSA2"); //调用SDK验证签名
        if (signVerified) {
            System.err.println("回滚的订单编号:" + out_trade_no);
            servletResponse.sendRedirect("http://127.0.0.1:8080/order");
        }
    }
    // 退款
    @RequestMapping("/refund")
    public Map<String,Object> refund(String no) throws AlipayApiException {
        Map<String,Object> map  = new HashMap<>();
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.eq("num", no);
        Order orderDB = orderService.getOne(wrapper);
        String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCLHDjvEn6fjWNOV/SGgkNv7B0AsgTA1WuKypedU2WIJ4hE5ZvJI5NiYgyh/O/HeFPYPhv+P7wv1+ATKGc5yyvb7I2b8/3MZhxFYMgn/4HjgJ9EGGfSSIkM5JqoZFHRuUNu5I1a9PqTW7SaReRdmXs3axVuHd+MYLh9KVGJyAiueAtQwq040EZHHz5lQ9oSj/R7zPEl4jXtXKHq4MC/tRl/wqAYeKwMeati9iUhK9BHYzxeTiV08l6UrMZteQ7duNxJTIg/uLQdgb7DwaWcBgy514Ra50PKmpXlHasIwhXfnW58aSpVjzdnbYu290JsGXW9h5CRp1VxZlqDfeg7zC95AgMBAAECggEAemldWFNIBZLfXiNb07lxKl31r5T3RdEID1vzSsgGQme3LBl4muipWxu4zhrLzRV/gnw1Gehv6xHl5jXZkCvO7nocqq1sGp+IJSzjNP9MByw5+iwXwB2ALE5GIgQVU53ZTw3jrbSBg4ZhSJhUOmFC4iNi+kFjZB4AenMPg1T4/rzqPPI17FO8MsW1CYiRmQcajkSDm37WXEayOaeSUXbDbxwUU96sNYU96kOuS92rX05hiZ+mX4XtKLqPuUVp0OKHe7WVEnf4sQp0nnZV4FjIS1MFk/dG24RfXBNtAkkA9m0NRxC0MTPEc2bwbjJ6bRyaETLMdzEN5d2qgCBYQzwJWQKBgQDQnHtx9Sg43XeSgqssRNaRkqC+naqyccorSfa5MTzxYfU1ByWlI4/SFfqHAbGTv2i6M191tOqffGHRHQUnyv8pSNwEbTUaQ7qZO03KiRc5cKw21U9UPrhUM8Lb5l7W2hrkpcLf5HOHOwqyMy215M84G8AsMb2vFhTD0+zh4fWHWwKBgQCqtf+kvhrO8bQQ69lgow/RB1g06VCM7I8a8WCWeLjbNwJujpcBmZA+ozVydmTsa3RNqaBfcUe6V5qQ8HCfLU4M9mKAd1cZSpP0/gwxjXBanT0pW2IaqfCJnwSZE602hNJhaDcIo0UEa94EK0TD67bpjqbPmrkt8GADjxGj3jfwuwKBgQCxSHrEErL2+IcYdIGhonKyzehbdcRN19QWy8moAocnH7dyNDuyxrD/ufvpZfASfrUyoFv8vR4zIVJDkyUBd0s3O5r3DBP32R0oAbUPbUfWYcGI6+h50L93l3F+zZ1Fe58lNxWQjiX8A5oIbOPo06Wiqjq7lB3+QIavEVgbcprlRwKBgB5D5A3uEkysN3NkjQaAAlbZyX6f+sLxUbHCJ5Cl2Igs6YZ2SzliY1tnjhs7+EqeBmGaeC9ToxbbXzDhH+ZontTXfcEu50c5nJ+rxremTswR4n0JkYwOovGTOrp07Zmj1LSmSFOEtsDA6vysOyXNDYG848Acu0X0vuECpWr7A8nvAoGBAJwEnEs86l0koOSo/DVVaVudJkMXhvl9GT7wG/MiEvknjTWosdVRgquEzGjmE25eK/4epOCONVU7bweloLSLbZ/OGGPGS4jHhJ3Kw/e4KBn4+nCi2g2/7CLfGvaydXmhgRy4qjIATfLceXtXaBNj7HIC1kCwzSphFwPzSM6DDEU8";
        String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgmJ5v66LB3uJiOa+/J+m1B3sgn1TNqO1Lh3gcfEoY3VIZCzqrGMCRrq0zk+EpOuchCZZJolN72AAWor97aLxCZzsVebhWZNvywCYW0fkbUF25uMKaIEQoNasPnLMToflhyzzmS4bLnamt+kv8xQf6XSM5FerT4J6w2JFFi1Vmmz0gTfGfqU6xpWDCjP6QZ7eOj/ujWvTgtavpG8cnsaqnakl1VeYkMW5jXGsOq16ms5W1/a9IsCw/Oe1Ez2vPKwEjxv+xxNpTdxcQm+Oq7Dc8/gAxkJBUy7SbI88II54MbY7qWDM+xKuY+WigOjNWiX/3m/M7RCrduLOV3Y8aGiBdQIDAQAB";
        AlipayConfig alipayConfig = new AlipayConfig();
        alipayConfig.setServerUrl("https://openapi-sandbox.dl.alipaydev.com/gateway.do");
        alipayConfig.setAppId("9021000130615233");
        alipayConfig.setPrivateKey(privateKey);
        alipayConfig.setFormat("json");
        alipayConfig.setAlipayPublicKey(alipayPublicKey);
        alipayConfig.setCharset("UTF-8");
        alipayConfig.setSignType("RSA2");
        AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        AlipayTradeRefundModel model = new AlipayTradeRefundModel();
        model.setRefundAmount(orderDB.getCarPrice()+"");
        model.setOutTradeNo(orderDB.getNum());
        request.setBizModel(model);
        AlipayTradeRefundResponse response = alipayClient.execute(request);
        System.out.println(response.getBody());
        if (response.isSuccess()) {
            System.out.println("调用成功");
            orderDB.setStat(2);
            orderDB.setRemark("已退款");
            boolean b = orderService.updateById(orderDB);
            map.put("flag",b);
            map.put("msg","退款成功");
            return map;
        } else {
            map.put("flag",false);
            map.put("msg","调用失败");
            return map;
        }
    }
    // 核对订单
    @RequestMapping("/check")
    public Map<String,Object> check() throws AlipayApiException {
        Map<String,Object> map  = new HashMap<>();
        List<Order> list = orderService.list();
        for (Order order : list) {
            String no = order.getNum();
            if(order.getStat()!=2){
                String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCLHDjvEn6fjWNOV/SGgkNv7B0AsgTA1WuKypedU2WIJ4hE5ZvJI5NiYgyh/O/HeFPYPhv+P7wv1+ATKGc5yyvb7I2b8/3MZhxFYMgn/4HjgJ9EGGfSSIkM5JqoZFHRuUNu5I1a9PqTW7SaReRdmXs3axVuHd+MYLh9KVGJyAiueAtQwq040EZHHz5lQ9oSj/R7zPEl4jXtXKHq4MC/tRl/wqAYeKwMeati9iUhK9BHYzxeTiV08l6UrMZteQ7duNxJTIg/uLQdgb7DwaWcBgy514Ra50PKmpXlHasIwhXfnW58aSpVjzdnbYu290JsGXW9h5CRp1VxZlqDfeg7zC95AgMBAAECggEAemldWFNIBZLfXiNb07lxKl31r5T3RdEID1vzSsgGQme3LBl4muipWxu4zhrLzRV/gnw1Gehv6xHl5jXZkCvO7nocqq1sGp+IJSzjNP9MByw5+iwXwB2ALE5GIgQVU53ZTw3jrbSBg4ZhSJhUOmFC4iNi+kFjZB4AenMPg1T4/rzqPPI17FO8MsW1CYiRmQcajkSDm37WXEayOaeSUXbDbxwUU96sNYU96kOuS92rX05hiZ+mX4XtKLqPuUVp0OKHe7WVEnf4sQp0nnZV4FjIS1MFk/dG24RfXBNtAkkA9m0NRxC0MTPEc2bwbjJ6bRyaETLMdzEN5d2qgCBYQzwJWQKBgQDQnHtx9Sg43XeSgqssRNaRkqC+naqyccorSfa5MTzxYfU1ByWlI4/SFfqHAbGTv2i6M191tOqffGHRHQUnyv8pSNwEbTUaQ7qZO03KiRc5cKw21U9UPrhUM8Lb5l7W2hrkpcLf5HOHOwqyMy215M84G8AsMb2vFhTD0+zh4fWHWwKBgQCqtf+kvhrO8bQQ69lgow/RB1g06VCM7I8a8WCWeLjbNwJujpcBmZA+ozVydmTsa3RNqaBfcUe6V5qQ8HCfLU4M9mKAd1cZSpP0/gwxjXBanT0pW2IaqfCJnwSZE602hNJhaDcIo0UEa94EK0TD67bpjqbPmrkt8GADjxGj3jfwuwKBgQCxSHrEErL2+IcYdIGhonKyzehbdcRN19QWy8moAocnH7dyNDuyxrD/ufvpZfASfrUyoFv8vR4zIVJDkyUBd0s3O5r3DBP32R0oAbUPbUfWYcGI6+h50L93l3F+zZ1Fe58lNxWQjiX8A5oIbOPo06Wiqjq7lB3+QIavEVgbcprlRwKBgB5D5A3uEkysN3NkjQaAAlbZyX6f+sLxUbHCJ5Cl2Igs6YZ2SzliY1tnjhs7+EqeBmGaeC9ToxbbXzDhH+ZontTXfcEu50c5nJ+rxremTswR4n0JkYwOovGTOrp07Zmj1LSmSFOEtsDA6vysOyXNDYG848Acu0X0vuECpWr7A8nvAoGBAJwEnEs86l0koOSo/DVVaVudJkMXhvl9GT7wG/MiEvknjTWosdVRgquEzGjmE25eK/4epOCONVU7bweloLSLbZ/OGGPGS4jHhJ3Kw/e4KBn4+nCi2g2/7CLfGvaydXmhgRy4qjIATfLceXtXaBNj7HIC1kCwzSphFwPzSM6DDEU8";
                String alipayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgmJ5v66LB3uJiOa+/J+m1B3sgn1TNqO1Lh3gcfEoY3VIZCzqrGMCRrq0zk+EpOuchCZZJolN72AAWor97aLxCZzsVebhWZNvywCYW0fkbUF25uMKaIEQoNasPnLMToflhyzzmS4bLnamt+kv8xQf6XSM5FerT4J6w2JFFi1Vmmz0gTfGfqU6xpWDCjP6QZ7eOj/ujWvTgtavpG8cnsaqnakl1VeYkMW5jXGsOq16ms5W1/a9IsCw/Oe1Ez2vPKwEjxv+xxNpTdxcQm+Oq7Dc8/gAxkJBUy7SbI88II54MbY7qWDM+xKuY+WigOjNWiX/3m/M7RCrduLOV3Y8aGiBdQIDAQAB";
                AlipayConfig alipayConfig = new AlipayConfig();
                alipayConfig.setServerUrl("https://openapi-sandbox.dl.alipaydev.com/gateway.do");
                alipayConfig.setAppId("9021000130615233");
                alipayConfig.setPrivateKey(privateKey);
                alipayConfig.setFormat("json");
                alipayConfig.setAlipayPublicKey(alipayPublicKey);
                alipayConfig.setCharset("UTF-8");
                alipayConfig.setSignType("RSA2");
                AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
                AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
                AlipayTradeQueryModel model = new AlipayTradeQueryModel();
                model.setOutTradeNo(no);
                request.setBizModel(model);
                AlipayTradeQueryResponse response = alipayClient.execute(request);
                System.out.println(response.getBody());
                if (response.isSuccess()) {
                    // 已支付,状态码未改
                    System.out.println("调用成功");
                    if(order.getStat()==1){
                        order.setRemark("已支付");
                    }else {
                        order.setRemark("已支付,状态码未改");
                    }
                } else {
                    // 未支付,状态码已改
                    System.out.println("调用失败");
                    if(order.getStat()==0){
                    }else {
                        order.setRemark("未支付,状态码已改");
                    }
                }
            }
        }
        boolean b = orderService.updateBatchById(list);
        if(b){
            map.put("flag",b);
            map.put("msg","校验成功");
            return map;
        }else {
            map.put("flag",false);
            map.put("msg","校验失败");
            return map;
        }
    }
}- mq消费者,MyListener.java
 
package com.llh.listener;
import cn.hutool.core.collection.CollUtil;
import com.llh.service.OrderService;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.List;
/**
 * User: lilinhan
 * DateTime: 2023/10/31 19:16
 */
@Component
public class MyListener implements MessageListenerOrderly {
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    OrderService orderService;
    // 初始化mq
    @PostConstruct
    public void init() throws MQClientException {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer();
        consumer.setNamesrvAddr("127.0.0.1:9876");
        consumer.subscribe("car2", "*");
        consumer.setConsumerGroup("dsf");
        consumer.setInstanceName("name");
        consumer.registerMessageListener(this);
        consumer.start();
    }
    // 接收的有序消息队列
    @Override
    public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
        if(CollUtil.isNotEmpty(msgs)){
            for (MessageExt msg : msgs) {
                String body = new String(msg.getBody());
                System.err.println("接受的消息--------"+body);
                return ConsumeOrderlyStatus.SUCCESS;
            }
        }else {
            System.err.println("消息为空");
        }
        return ConsumeOrderlyStatus.SUCCESS;
    }
}3.6、模拟抢单
- 配置类,MybatisPlusConfig.java
 
package com.llh.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
/**
 * User: lilinhan
 * DateTime: 2023/10/13 11:53
 */
@Configuration
@MapperScan(value = {"com.llh.mapper"})
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        // 分页拦截器
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return mybatisPlusInterceptor;
    }
    @Bean
    public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(5);// 核心线程数量
        taskExecutor.setMaxPoolSize(10);// 最大线程数量
        taskExecutor.setKeepAliveSeconds(2);// 设置时长(秒)
        taskExecutor.setQueueCapacity(10);// 设置队列容量
        taskExecutor.setThreadNamePrefix("llh-thread:");// 线程名前缀
        return taskExecutor;
    }
}- ThreadController.java
 
package com.llh.controller;
import cn.hutool.core.lang.Snowflake;
import com.llh.domain.Takeaway;
import com.llh.service.TakeawayService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.util.*;
/**
 * User: lilinhan
 * DateTime: 2023/11/2 14:04
 */
@RestController
@RequestMapping("/thread")
public class ThreadController {
    @Autowired
    TakeawayService takeawayService;
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Autowired
    RedissonClient redissonClient;
    private int count = 1;
    // 生成订单
    @RequestMapping("/test")
    public boolean test(){
        for (int i = 0; i < 3; i++) {
            threadPoolTaskExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    List<Takeaway> list = new ArrayList<>();
                    for (int i1 = 0; i1 < 5; i1++) {
                        System.out.println("当前线程-----"+Thread.currentThread().getId()+":"+Thread.currentThread().getName());
                        // 生成实体
                        Takeaway takeaway = new Takeaway();
                        takeaway.setPrice(new BigDecimal(20.22));
                        takeaway.setFroms(new Snowflake().nextIdStr());
                        takeaway.setTos(new Snowflake().nextIdStr());
                        takeaway.setCreated(new Date());
                        list.add(takeaway);
                    }
                    takeawayService.saveBatch(list);
                }
            });
        }
        return true;
    }
    // 测试抢单
    @RequestMapping("/size")
    public Map<String,Object> size(String no){
        Map<String,Object> map = new HashMap<>();
        System.err.println("当前用户:"+Thread.currentThread().getId()+",正在抢购商品:"+no);
//        Boolean b1 = redisTemplate.opsForValue().setIfAbsent(no, no, 2, TimeUnit.SECONDS);
        RLock lock = redissonClient.getLock(no);
        if(lock.tryLock()){
            try {
                // 通过id查询订单
                Takeaway takeaway = takeawayService.getById(no);
                takeaway.setStat(1);// 已抢
                Thread.sleep(3000);
                boolean b = takeawayService.updateById(takeaway);
                if(takeaway.getStat()==1){
                    System.err.println("此商品已被抢购");
                }
                if(b){
                    System.err.println("恭喜用户:"+Thread.currentThread().getId()+",@@@@@@成功抢购到商品:"+no);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }finally {
                if(lock!=null && lock.isHeldByCurrentThread()){
                    lock.unlock();;
                }
            }
        }else {
            System.err.println("抢购结束");
        }
        return map;
    }
}4、前端代码
- 用户登录,LoginView.vue
 
<script>
import qs from "qs";
export default {
    data(){
        return{
            formInline:{},
            formInline2:{},
            formInline3:{},
            activeName: 'easyLogin',
            codeUrl:'http://127.0.0.1:9999/user-api/user/getCode',
            rules:{
                name:[
                    { required: true, message: '请输入账号', trigger: 'blur' },
                ],
                phone:[
                    { required: true, message: '请输入手机号', trigger: 'blur' },
                ],
                pwd:[
                    { required: true, message: '请输入密码', trigger: 'blur' },
                ],
                code:[
                    { required: true, message: '请输入验证码', trigger: 'blur' },
                ],
                email:[
                    { required: true, message: '请输入邮箱', trigger: 'blur' },
                ]
            }
        }
    },
    methods:{
        handleClick(tab, event) {
            console.log(tab, event);
        },
        newCode(){
            this.codeUrl = 'http://127.0.0.1:9999/user-api/user/getCode?time='+new Date().getTime();
        },
        onSubmit(loginForm){
            this.$refs[loginForm].validate((val)=>{
                if(val){
                    this.axios.post("/user-api/user/login",qs.stringify(this.formInline)).then(res=>{
                        if(res.data.code==1001){
                            this.$message.success(res.data.msg);
                            localStorage.setItem("token",res.data.token);
                            localStorage.setItem("loginName", res.data.loginName)
                            localStorage.setItem("userId", res.data.userId)
                            this.$router.push("/good");
                        }else{
                            this.$message.error(res.data.msg);
                            this.newCode();
                            this.formInline.code = '';
                        }
                    })
                }else {
                    return false;
                }
            })
        },
        sendSms(tel){
            this.axios.post("/user-api/user/sendSms?phone="+tel).then(res=>{
                if(res.data.code==1001){
                    this.$message.success(res.data.msg);
                }else {
                    this.$message.error(res.data.msg);
                }
            })
        },
        onSubmit2(loginForm){
            this.$refs[loginForm].validate((val)=>{
                if(val){
                    this.axios.post("/user-api/user/phoneLogin",qs.stringify(this.formInline2)).then(res=>{
                        if(res.data.code==1001){
                            this.$message.success(res.data.msg);
                            localStorage.setItem("token",res.data.token);
                            this.$router.push("/list");
                        }else{
                            this.$message.error(res.data.msg);
                            this.formInline2.code = '';
                        }
                    })
                }else {
                    return false;
                }
            })
        },
        Register(loginForm){
            this.$refs[loginForm].validate((val)=>{
                if(val){
                    this.axios.post("/user-api/user/register",qs.stringify(this.formInline3)).then(res=>{
                        if(res.data.code==1001){
                            this.$message.success(res.data.msg);
                            this.activeName = "easyLogin";
                        }else {
                            this.$message.error(res.data.msg);
                        }
                    })
                }else {
                    return false;
                }
            })
        },
        resetForm(formName) {
            this.$refs[formName].resetFields();
        }
    },
    created() {
    
    }
}
</script>
<template>
    <div>
        <el-tabs v-model="activeName" @tab-click="handleClick">
            <el-tab-pane label="普通登录" name="easyLogin">
                <el-form :inline="true" :rules="rules" ref="formInline" :model="formInline" class="demo-form-inline">
                    <el-form-item label="账号" prop="name">
                        <el-input v-model="formInline.name" placeholder="账号"></el-input>
                    </el-form-item><br>
                    
                    <el-form-item label="密码" prop="pwd">
                        <el-input v-model="formInline.pwd" placeholder="密码"></el-input>
                    </el-form-item><br>
                    
                    <el-form-item label="验证码" prop="code">
                        <el-input v-model="formInline.code" placeholder="验证码"></el-input>
                    </el-form-item><br>
                    
                    <el-form-item>
                        <el-image @click="newCode" :src="codeUrl"></el-image>
                    </el-form-item><br>
                    
                    <el-form-item>
                        <el-button type="primary" @click="onSubmit('formInline')">登录</el-button>
                        <el-button @click="resetForm('formInline')">重置</el-button>
                    </el-form-item>
                </el-form>
            </el-tab-pane>
            
            <el-tab-pane label="手机号加验证码登录" name="telAndYzm">
                <el-form :inline="true" :rules="rules" ref="formInline2" :model="formInline2" class="demo-form-inline">
                    <el-form-item label="手机号" prop="phone">
                        <el-input v-model="formInline2.phone" placeholder="手机号"></el-input>
                    </el-form-item><br>
                    
                    <el-form-item label="验证码" prop="code">
                        <el-input v-model="formInline2.code" placeholder="验证码"></el-input>
                    </el-form-item><br>
                    
                    <el-form-item>
                        <el-button type="primary" @click="sendSms(formInline2.phone)">发送短信</el-button>
                        <el-button type="primary" @click="onSubmit2('formInline2')">登录</el-button>
                    </el-form-item>
                </el-form>
            </el-tab-pane>
            
            <el-tab-pane label="邮箱注册" name="register">
                <el-form :inline="true" :rules="rules" ref="formInline3" :model="formInline3" class="demo-form-inline">
                    <el-form-item label="邮箱" prop="email">
                        <el-input v-model="formInline3.email" placeholder="QQ号">
                            <template slot="append">@qq.com</template>
                        </el-input>
                    </el-form-item><br>
                    
                    <el-form-item label="手机号" prop="phone">
                        <el-input v-model="formInline3.phone" placeholder="手机号"></el-input>
                    </el-form-item><br>
                    
                    <el-form-item label="验证码" prop="code">
                        <el-input v-model="formInline3.code" placeholder="验证码"></el-input>
                    </el-form-item><br>
                    
                    <el-form-item>
                        <el-image @click="newCode" :src="codeUrl"></el-image>
                    </el-form-item><br>
                    
                    <el-form-item>
                        <el-button type="primary" @click="Register('formInline3')">注册</el-button>
                        <el-button @click="resetForm('formInline3')">重置</el-button>
                    </el-form-item>
                </el-form>
            </el-tab-pane>
        </el-tabs>
    
    </div>
</template>
<style scoped>
</style>- 车辆列表,ListView.vue
 
<script>
import qs from "qs";
export default {
    data(){
        return{
            formInline:{},
            current:1,
            size:3,
            models:[],
            brands:[],
            dists:[],
            colors:[],
            tableData:[],
            total:0,
            addDialogFormVisible:false,
            updateDialogFormVisible:false,
            orderDialogFormVisible:false,
            formLabelWidth:'150px',
            addForm:{},
            updateForm:{
                imgUrl:''
            },
            orderForm:{
                userPhone:'',
                carStart:'',
                carEnd:'',
                carRent:0,
            },
            imageUrl:'',
            value:'',
        }
    },
    methods:{
        updateCar(){
            this.axios.post("/car-api/car/update",qs.stringify(this.updateForm)).then(res=>{
                let flag = res.data.flag;
                if(flag){
                    this.$message.success(res.data.msg);
                    this.updateDialogFormVisible = false;
                    this.onSubmit();
                }else {
                    this.$message.success(res.data.msg);
                }
            })
        },
        openUpdate(row){
            this.updateForm = row;
            this.imageUrl = row.imgUrl;
            this.updateDialogFormVisible = true;
        },
        delCar(id){
            this.axios.post("/car-api/car/del?id="+id).then(res=>{
                if(res.data.flag){
                    this.$message.success(res.data.msg);
                    this.onSubmit();
                }else {
                    this.$message.success(res.data.msg);
                }
            })
        },
        saveOrder(){
            this.orderForm.carStart = this.value[0];
            this.orderForm.carEnd = this.value[1];
            this.axios.post("/car-api/order/save",qs.stringify(this.orderForm)).then(res=>{
                let flag = res.data.flag;
                if(flag){
                    this.$message.success(res.data.msg);
                    this.orderDialogFormVisible = false;
                    this.$router.push("/order")
                }else {
                    this.$message.success(res.data.msg);
                }
            })
        },
        goCar(row){
            this.orderForm.carNo = row.num;
            this.orderForm.userPhone = row.userPhone;
            this.orderForm.carRent = row.rent;
            this.orderDialogFormVisible = true;
        },
        openAdd(){
            this.addForm = {};
            this.addDialogFormVisible = true;
        },
        saveCar(){
            this.axios.post("/car-api/car/save",qs.stringify(this.addForm)).then(res=>{
                let flag = res.data.flag;
                if(flag){
                    this.$message.success(res.data.msg);
                    this.addDialogFormVisible = false;
                    this.onSubmit();
                }else {
                    this.$message.success(res.data.msg);
                }
            })
        },
        handleAvatarSuccess(res, file) {
            this.imageUrl = URL.createObjectURL(file.raw);
            this.addForm.imgUrl = res.url;
            this.updateForm.imgUrl = res.url;
        },
        handleSizeChange(val) {
            console.log(`每页 ${val} 条`);
            this.size = val;
            this.onSubmit();
        },
        handleCurrentChange(val) {
            console.log(`当前页: ${val}`);
            this.current = val;
            this.onSubmit();
        },
        initList(){
            this.axios.get("/car-api/dist/list").then(res=>{
                this.models = res.data.models;
                this.brands = res.data.brands;
                this.dists = res.data.dists;
                this.colors = res.data.colors;
            })
        },
        onSubmit(){
            this.axios.post("/car-api/car/list?current="+this.current+"&size="+this.size,qs.stringify(this.formInline)).then(res=>{
                this.tableData = res.data.records;
                this.total = res.data.total;
            })
        }
    },
    created() {
        this.initList();
        this.onSubmit();
    }
}
</script>
<template>
<div>
    <el-form :inline="true" :model="formInline" class="demo-form-inline">
        <el-form-item label="车辆品牌">
            <el-select v-model="formInline.brandId" placeholder="车辆品牌" clearable="clearable">
                <el-option v-for="c in brands" :key="c.id" :label="c.distValue" :value="c.id"></el-option>
            </el-select>
        </el-form-item>
        <el-form-item label="车辆型号">
            <el-select v-model="formInline.modelId" placeholder="车辆型号" clearable="clearable">
                <el-option v-for="c in models" :key="c.id" :label="c.distValue" :value="c.id"></el-option>
            </el-select>
        </el-form-item>
        <el-form-item>
            <el-button type="primary" @click="onSubmit">查询</el-button>
            <el-button type="primary" @click="openAdd">添加</el-button>
        </el-form-item>
    </el-form>
    
    <el-table
        :data="tableData"
        border
        style="width: 100%">
        <el-table-column prop="num" label="车辆编号" width="180"></el-table-column>
        <el-table-column prop="modelName" label="型号" width="100"></el-table-column>
        <el-table-column prop="brandName" label="品牌" width="100"></el-table-column>
        <el-table-column prop="outs" label="排量(T)" width="100">
            <template v-slot="scope">
                {{scope.row.outs}}.0
            </template>
        </el-table-column>
        <el-table-column prop="speed" label="变速箱" width="100"></el-table-column>
        <el-table-column prop="years" label="年代" width="100"></el-table-column>
        <el-table-column prop="rent" label="日租金" width="100"></el-table-column>
        <el-table-column prop="imgUrl" label="图片" width="150">
            <template v-slot="scope">
                <el-image :src="scope.row.imgUrl" style="width: 80px;height: 80px"></el-image>
            </template>
        </el-table-column>
        <el-table-column prop="detail" label="介绍" width="180"></el-table-column>
        <el-table-column prop="stat" label="状态" width="100">
            <template v-slot="scope">
                <span v-if="scope.row.stat==0" style="color: green">空闲</span>
                <span v-if="scope.row.stat==1" style="color: orange">出租</span>
            </template>
        </el-table-column>
        <el-table-column label="操作" width="150">
            <template v-slot="scope">
                <el-button type="warning" size="mini" @click="openUpdate(scope.row)">编辑</el-button>
                <el-button type="danger" size="mini" @click="delCar(scope.row.id)">删除</el-button>
                <span v-if="scope.row.stat==0">
                    <el-button type="primary" size="mini" @click="goCar(scope.row)">租车</el-button>
                </span>
            </template>
        </el-table-column>
    </el-table>
    
    <el-pagination
        background
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="current"
        :page-sizes="[3, 6, 9, 12]"
        :page-size="size"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total">
    </el-pagination>
    
    <el-dialog title="车辆录入" :visible.sync="addDialogFormVisible">
        <el-form :model="addForm">
            <el-form-item label="车辆信息:" :label-width="formLabelWidth">
                <el-select v-model="addForm.brandId" placeholder="请选择车辆品牌" clearable="clearable">
                    <el-option v-for="c in brands" :key="c.id" :label="c.distValue" :value="c.id"></el-option>
                </el-select>
                <el-select v-model="addForm.distId" placeholder="请选择车系" clearable="clearable">
                    <el-option v-for="c in dists" :key="c.id" :label="c.distValue" :value="c.id"></el-option>
                </el-select>
                <el-select v-model="addForm.modelId" placeholder="请选择车辆型号" clearable="clearable">
                    <el-option v-for="c in models" :key="c.id" :label="c.distValue" :value="c.id"></el-option>
                </el-select>
            </el-form-item>
            
            <el-form-item label="预售价格:" :label-width="formLabelWidth">
                <el-input v-model="addForm.price" autocomplete="off" clearable="clearable"></el-input>
            </el-form-item>
            
            <el-form-item label="行驶里程:" :label-width="formLabelWidth">
                <el-input v-model="addForm.mileage" autocomplete="off" clearable="clearable"></el-input>
            </el-form-item>
            
            <el-form-item label="车辆颜色:" :label-width="formLabelWidth">
                <el-radio-group v-model="addForm.colorId">
                    <el-radio v-for="c in colors" :label="c.id" >{{c.distValue}}</el-radio>
                </el-radio-group>
            </el-form-item>
            
            <el-form-item label="联系人:" :label-width="formLabelWidth">
                <el-input v-model="addForm.userName" autocomplete="off" clearable="clearable"></el-input>
            </el-form-item>
            
            <el-form-item label="手机号:" :label-width="formLabelWidth">
                <el-input v-model="addForm.userPhone" autocomplete="off" clearable="clearable"></el-input>
            </el-form-item>
            
            <el-form-item label="车辆介绍:" :label-width="formLabelWidth">
                <el-input type="textarea" v-model="addForm.detail" clearable="clearable"></el-input>
            </el-form-item>
            
            <el-form-item label="车辆图片:" :label-width="formLabelWidth">
                <el-upload
                    class="avatar-uploader"
                    action="http://127.0.0.1:9999/car-api/upload/minio"
                    :show-file-list="false"
                    :on-success="handleAvatarSuccess">
                    <img v-if="imageUrl" :src="imageUrl" class="avatar">
                    <i v-else class="el-icon-plus avatar-uploader-icon"></i>
                </el-upload>
            </el-form-item>
        </el-form>
        <div slot="footer" class="dialog-footer">
            <el-button @click="addDialogFormVisible = false">取 消</el-button>
            <el-button type="primary" @click="saveCar('addForm')">确 定</el-button>
        </div>
    </el-dialog>
    
    <!--车辆修改-->
    <el-dialog title="车辆修改" :visible.sync="updateDialogFormVisible">
        <el-form :model="updateForm">
            <el-form-item label="车辆信息:" :label-width="formLabelWidth">
                <el-select v-model="updateForm.brandId" placeholder="请选择车辆品牌" clearable="clearable">
                    <el-option v-for="c in brands" :key="c.id" :label="c.distValue" :value="c.id"></el-option>
                </el-select>
                <el-select v-model="updateForm.distId" placeholder="请选择车系" clearable="clearable">
                    <el-option v-for="c in dists" :key="c.id" :label="c.distValue" :value="c.id"></el-option>
                </el-select>
                <el-select v-model="updateForm.modelId" placeholder="请选择车辆型号" clearable="clearable">
                    <el-option v-for="c in models" :key="c.id" :label="c.distValue" :value="c.id"></el-option>
                </el-select>
            </el-form-item>
            
            <el-form-item label="预售价格:" :label-width="formLabelWidth">
                <el-input v-model="updateForm.price" autocomplete="off" clearable="clearable"></el-input>
            </el-form-item>
            
            <el-form-item label="行驶里程:" :label-width="formLabelWidth">
                <el-input v-model="updateForm.mileage" autocomplete="off" clearable="clearable"></el-input>
            </el-form-item>
            
            <el-form-item label="车辆颜色:" :label-width="formLabelWidth">
                <el-radio-group v-model="updateForm.colorId">
                    <el-radio v-for="c in colors" :label="c.id" >{{c.distValue}}</el-radio>
                </el-radio-group>
            </el-form-item>
            
            <el-form-item label="联系人:" :label-width="formLabelWidth">
                <el-input v-model="updateForm.userName" autocomplete="off" clearable="clearable"></el-input>
            </el-form-item>
            
            <el-form-item label="手机号:" :label-width="formLabelWidth">
                <el-input v-model="updateForm.userPhone" autocomplete="off" clearable="clearable"></el-input>
            </el-form-item>
            
            <el-form-item label="车辆介绍:" :label-width="formLabelWidth">
                <el-input type="textarea" v-model="updateForm.detail" clearable="clearable"></el-input>
            </el-form-item>
            
            <el-form-item label="车辆图片:" :label-width="formLabelWidth">
                <el-upload
                    class="avatar-uploader"
                    action="http://127.0.0.1:9999/car-api/upload/minio"
                    :show-file-list="false"
                    :on-success="handleAvatarSuccess">
                    <img v-if="imageUrl" :src="imageUrl" class="avatar">
                    <i v-else class="el-icon-plus avatar-uploader-icon"></i>
                </el-upload>
            </el-form-item>
        </el-form>
        <div slot="footer" class="dialog-footer">
            <el-button @click="updateDialogFormVisible = false">取 消</el-button>
            <el-button type="primary" @click="updateCar('updateForm')">确 定</el-button>
        </div>
    </el-dialog>
    
    <el-dialog title="租车详情" :visible.sync="orderDialogFormVisible">
        <el-form :model="orderForm">
            <el-form-item label="车辆编号" :label-width="formLabelWidth">
                <el-input v-model="orderForm.carNo" autocomplete="off"></el-input>
            </el-form-item>
            
            <el-form-item label="租金/天" :label-width="formLabelWidth">
                <el-input v-model="orderForm.carRent" autocomplete="off"></el-input>
            </el-form-item>
            
            <el-form-item label="租用天数" :label-width="formLabelWidth">
                <el-date-picker
                    v-model="value"
                    type="daterange"
                    value-format="yyyy-MM-dd"
                    range-separator="至"
                    start-placeholder="开始日期"
                    end-placeholder="结束日期">
                </el-date-picker>
            </el-form-item>
        </el-form>
        <div slot="footer" class="dialog-footer">
            <el-button @click="orderDialogFormVisible = false">取 消</el-button>
            <el-button type="primary" @click="saveOrder">确 定</el-button>
        </div>
    </el-dialog>
</div>
</template>
<style>
.avatar-uploader .el-upload {
    border: 1px dashed #d9d9d9;
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
}
.avatar-uploader .el-upload:hover {
    border-color: #409EFF;
}
.avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 178px;
    height: 178px;
    line-height: 178px;
    text-align: center;
}
.avatar {
    width: 178px;
    height: 178px;
    display: block;
}
</style>- 订单列表,OrderView.vue
 
<script>
export default {
    data(){
        return{
            tableData:[],
            
        }
    },
    methods:{
        check(){
            this.axios.get("/car-api/pay/check").then(res=>{
                if(res.data.flag){
                    this.$message.success(res.data.msg);
                    this.initList();
                }else {
                    this.$message.error(res.data.msg);
                }
            })
        },
        refund(no){
            this.axios.get("/car-api/pay/refund?no="+no).then(res=>{
                if(res.data.flag){
                    this.$message.success(res.data.msg);
                    this.initList();
                }else {
                    this.$message.error(res.data.msg);
                }
            })
        },
        goPay(no){
            window.location.href="http://localhost:9999/car-api/pay/goPay?no="+no;
        },
        initList(){
            this.axios.get("/car-api/order/list").then(res=>{
                this.tableData = res.data;
            })
        },
    },
    created() {
        this.initList();
    }
}
</script>
<template>
<div>
    <el-button type="primary" @click="check">校验订单</el-button>
    <el-image src="https://llh-images.oss-cn-beijing.aliyuncs.com/test/6454bb7c-55e7-4ae7-808c-5c6c5204f0e2.jpg" style="width: 100px;height: 100px"></el-image>
    <el-image src="https://llh-images.oss-cn-beijing.aliyuncs.com/bdaffc4d-cdac-46dd-85a4-ea36cecb122b.jpg" style="width: 100px;height: 100px"></el-image>
    
    <el-table
        :data="tableData"
        border
        style="width: 100%">
        <el-table-column prop="num" label="订单编号" width="180"></el-table-column>
        <el-table-column prop="carNo" label="车辆编号" width="180"></el-table-column>
        <el-table-column prop="carRent" label="租金/天" width="150"></el-table-column>
        <el-table-column prop="carPrice" label="费用" width="150"></el-table-column>
        <el-table-column prop="userPhone" label="联系人电话" width="150"></el-table-column>
        <el-table-column prop="remark" label="说明" width="180">
            <template v-slot="scope">
                <span v-if="scope.row.remark=='已支付,状态码未改'" style="color: orange">
                    {{scope.row.remark}}
                    <el-button type="warning" size="mini" @click="goUpdate(scope.row.num)">去修改</el-button>
                </span>
                <span v-if="scope.row.remark=='未支付,状态码已改'" style="color: orange">
                    {{scope.row.remark}}
                    <el-button type="warning" size="mini" @click="goPay(scope.row.num)">去支付</el-button>
                </span>
                <span v-if="scope.row.remark=='已支付'" style="color: green">
                    {{scope.row.remark}}
                </span>
                <span v-if="scope.row.remark=='已退款'" style="color: red">
                    {{scope.row.remark}}
                </span>
            </template>
        </el-table-column>
        <el-table-column prop="createTime" label="提交时间" width="180"></el-table-column>
        <el-table-column label="操作" width="150">
            <template v-slot="scope">
                <span v-if="scope.row.stat==0">
                    <el-button type="primary" size="mini" @click="goPay(scope.row.num)">待支付</el-button>
                </span>
                <span v-if="scope.row.stat==1">
                    <el-button type="danger" size="mini" @click="refund(scope.row.num)">去退款</el-button>
                </span>
                <span v-if="scope.row.stat==2" style="color: red">
                    已退款
                </span>
            </template>
        </el-table-column>
    </el-table>
</div>
</template>
<style scoped>
</style>
                            
                            
126 comments
2025年10月新盘 做第一批吃螃蟹的人coinsrore.com
新车新盘 嘎嘎稳 嘎嘎靠谱coinsrore.com
新车首发,新的一年,只带想赚米的人coinsrore.com
新盘 上车集合 留下 我要发发 立马进裙coinsrore.com
做了几十年的项目 我总结了最好的一个盘(纯干货)coinsrore.com
新车上路,只带前10个人coinsrore.com
新盘首开 新盘首开 征召客户!!!coinsrore.com
新项目准备上线,寻找志同道合 的合作伙伴coinsrore.com
新车即将上线 真正的项目,期待你的参与coinsrore.com
新盘新项目,不再等待,现在就是最佳上车机会!coinsrore.com
新盘新盘 这个月刚上新盘 新车第一个吃螃蟹!coinsrore.com
2025年10月新盘 做第一批吃螃蟹的人coinsrore.com
新车新盘 嘎嘎稳 嘎嘎靠谱coinsrore.com
新车首发,新的一年,只带想赚米的人coinsrore.com
新盘 上车集合 留下 我要发发 立马进裙coinsrore.com
做了几十年的项目 我总结了最好的一个盘(纯干货)coinsrore.com
新车上路,只带前10个人coinsrore.com
新盘首开 新盘首开 征召客户!!!coinsrore.com
新项目准备上线,寻找志同道合 的合作伙伴coinsrore.com
新车即将上线 真正的项目,期待你的参与coinsrore.com
新盘新项目,不再等待,现在就是最佳上车机会!coinsrore.com
新盘新盘 这个月刚上新盘 新车第一个吃螃蟹!coinsrore.com
2025年10月新盘 做第一批吃螃蟹的人coinsrore.com
新车新盘 嘎嘎稳 嘎嘎靠谱coinsrore.com
新车首发,新的一年,只带想赚米的人coinsrore.com
新盘 上车集合 留下 我要发发 立马进裙coinsrore.com
做了几十年的项目 我总结了最好的一个盘(纯干货)coinsrore.com
新车上路,只带前10个人coinsrore.com
新盘首开 新盘首开 征召客户!!!coinsrore.com
新项目准备上线,寻找志同道合 的合作伙伴coinsrore.com
新车即将上线 真正的项目,期待你的参与coinsrore.com
新盘新项目,不再等待,现在就是最佳上车机会!coinsrore.com
新盘新盘 这个月刚上新盘 新车第一个吃螃蟹!coinsrore.com
2025年10月新盘 做第一批吃螃蟹的人coinsrore.com
新车新盘 嘎嘎稳 嘎嘎靠谱coinsrore.com
新车首发,新的一年,只带想赚米的人coinsrore.com
新盘 上车集合 留下 我要发发 立马进裙coinsrore.com
做了几十年的项目 我总结了最好的一个盘(纯干货)coinsrore.com
新车上路,只带前10个人coinsrore.com
新盘首开 新盘首开 征召客户!!!coinsrore.com
新项目准备上线,寻找志同道合 的合作伙伴coinsrore.com
新车即将上线 真正的项目,期待你的参与coinsrore.com
新盘新项目,不再等待,现在就是最佳上车机会!coinsrore.com
新盘新盘 这个月刚上新盘 新车第一个吃螃蟹!coinsrore.com
新项目准备上线,寻找志同道合的合作伙伴coinsrore.com
新盘新盘 这个月刚上新盘 新车第一个吃螃蟹!coinsrore.com
2025年10月新盘 做第一批吃螃蟹的人
新项目准备上线,寻找志同道合的合作伙伴
新车上路,只带前10个人
新盘新项目,不再等待,现在就是最佳上车机会!
新车上路,只带前10个人coinsrore.com
做了几十年的项目 我总结了最好的一个盘(纯干货)
新盘新盘 这个月刚上新盘 新车第一个吃螃蟹!coinsrore.com
新车新盘 嘎嘎稳 嘎嘎靠谱
做了几十年的项目 我总结了最好的一个盘(纯干货)
熊猫计划
扭曲的虔诚
中华英雄之风云再起
欲血僵尸
波哥大